public void GetSplitPoints_OnlySpillsRight_SplitPointLengthIsCorrect() { Scope root = new Scope("012345"); root.DefineInnerScope(3, 3); //012|345 List <SplitPoint> points = splitter.GetSplitPoints(root, 0, 5); //(012|34)5) Assert.AreEqual(1, points.Count); SplitPoint firstPoint = points[0]; Assert.AreEqual(3, firstPoint.StartIndex); Assert.AreEqual(2, firstPoint.Length); }
public void Create(Vector2 field) { var max = new Vector2(this.sectionX.y, this.sectionY.y); var pos = Vector2.zero; var step = Vector2.zero; var counter = 0; while (counter < 2) { (pos - field) .EachAction((i, p) => pos[i] >= 0f, (i, p) => { var isFrame = (p <= -field[i] == true || p >= field[i] == true); var isMain = Random.value < this.main.Rate && isFrame == false; p += 0.5f * (isMain == true ? max[i] : 0f); p = p < field[i] - max[i] ? p : field[i]; var isLast = (p >= field[i]); isMain = (isMain || isFrame || isLast); var sp = new SplitPoint(p, isMain == true ? this.main.Width : this.sub.Width); (i == 0 ? this.PointsX : this.PointsY).Add(sp); step[i] = (isMain == true ? max[i] : 0f); if (isLast == true) { counter++; pos[i] = -1f; } }); step += new Vector2(this.sectionX.Rand(), this.sectionY.Rand()); pos = pos.EachFunc(step, (v1, Vector2) => v1 + (v1 < 0f ? 0f : Vector2)); } }
// ThreadPool::available_slave() tries to find an idle thread which is available // to join SplitPoint 'sp'. internal static Thread available_slave(SplitPoint sp) { return threads.FirstOrDefault(t => t.can_join(sp)); }
// Thread::split() does the actual work of distributing the work at a node between // several available threads. If it does not succeed in splitting the node // (because no idle threads are available), the function immediately returns. // If splitting is possible, a SplitPoint object is initialized with all the // data that must be copied to the helper threads and then helper threads are // informed that they have been assigned work. This will cause them to instantly // leave their idle loops and call search(). When all threads have returned from // search() then split() returns. internal void split( Position pos, StackArrayWrapper ss, ValueT alpha, ValueT beta, ref ValueT bestValue, ref MoveT bestMove, Depth depth, int moveCount, MovePicker movePicker, NodeType nodeType, bool cutNode) { Debug.Assert(searching); Debug.Assert( -Value.VALUE_INFINITE < bestValue && bestValue <= alpha && alpha < beta && beta <= Value.VALUE_INFINITE); Debug.Assert(depth >= ThreadPool.minimumSplitDepth); Debug.Assert(splitPointsSize < _.MAX_SPLITPOINTS_PER_THREAD); // Pick and init the next available split point var sp = splitPoints[splitPointsSize]; ThreadHelper.lock_grab(sp.spinLock); // No contention here until we don't increment splitPointsSize sp.master = this; sp.parentSplitPoint = activeSplitPoint; sp.slavesMask = 0; sp.slavesMask = (1u << idx); sp.depth = depth; sp.bestValue = bestValue; sp.bestMove = bestMove; sp.alpha = alpha; sp.beta = beta; sp.nodeType = nodeType; sp.cutNode = cutNode; sp.movePicker = movePicker; sp.moveCount = moveCount; sp.pos = pos; sp.nodes = 0; sp.cutoff = false; sp.ss = ss; sp.allSlavesSearching = true; // Must be set under lock protection ++splitPointsSize; activeSplitPoint = sp; activePosition = null; // Try to allocate available threads Thread slave; while (Bitcount.popcount_Full(sp.slavesMask) < _.MAX_SLAVES_PER_SPLITPOINT && (slave = ThreadPool.available_slave(sp)) != null) { ThreadHelper.lock_grab(slave.spinlock); if (slave.can_join(activeSplitPoint)) { activeSplitPoint.slavesMask |= 1u << (slave.idx); slave.activeSplitPoint = activeSplitPoint; slave.searching = true; } ThreadHelper.lock_release(slave.spinlock); } // Everything is set up. The master thread enters the idle loop, from which // it will instantly launch a search, because its 'searching' flag is set. // The thread will return from the idle loop when all slaves have finished // their work at this split point. ThreadHelper.lock_release(sp.spinLock); base_idle_loop(null); // Force a call to base class idle_loop() // In the helpful master concept, a master can help only a sub-tree of its // split point and because everything is finished here, it's not possible // for the master to be booked. Debug.Assert(!searching); Debug.Assert(activePosition == null); // We have returned from the idle loop, which means that all threads are // finished. Note that decreasing splitPointsSize must be done under lock // protection to avoid a race with Thread::can_join(). ThreadHelper.lock_grab(spinlock); searching = true; --splitPointsSize; activeSplitPoint = sp.parentSplitPoint; activePosition = pos; ThreadHelper.lock_release(spinlock); // Split point data cannot be changed now, so no need to lock protect pos.set_nodes_searched(pos.nodes_searched() + sp.nodes); bestMove = Move.Create(sp.bestMove); bestValue = Value.Create(sp.bestValue); }
// Make a local copy to be sure doesn't become zero under our feet while // Thread::can_join() checks whether the thread is available to join the split // point 'sp'. An obvious requirement is that thread must be idle. With more than // two threads, this is not sufficient: If the thread is the master of some split // point, it is only available as a slave for the split points below his active // one (the "helpful master" concept in YBWC terminology). internal bool can_join(SplitPoint sp) { if (searching) { return false; } // Make a local copy to be sure it doesn't become zero under our feet while // testing next condition and so leading to an out of bounds access. var size = splitPointsSize; // No split points means that the thread is available as a slave for any // other thread otherwise apply the "helpful master" concept if possible. var bitIsSet = (splitPoints[size - 1].slavesMask & (1u << sp.master.idx)) != 0; //splitPoints[size - 1].slavesMask.test(sp.master.idx) return size > 0 || bitIsSet; }
internal void base_idle_loop(ManualResetEvent initEvent) { // Pointer 'this_sp' is not null only if we are called from split(), and not // at the thread creation. This means we are the split point's master. var this_sp = splitPointsSize > 0 ? activeSplitPoint : null; Debug.Assert(this_sp == null || (this_sp.master == this && searching)); while (!exit && this_sp == null && (this_sp.slavesMask == 0)) { // If this thread has been assigned work, launch a search while (searching) { ThreadHelper.lock_grab(spinlock); Debug.Assert(activeSplitPoint != null); var sp = activeSplitPoint; ThreadHelper.lock_release(spinlock); var stack = new StackArrayWrapper(new Stack[_.MAX_PLY + 4]); var ss = new StackArrayWrapper(stack.table, 2); var pos = new Position(sp.pos, this); Array.Copy(sp.ss.table, ss.table, 5); ss[ss.current].splitPoint = sp; ThreadHelper.lock_grab(sp.spinLock); Debug.Assert(activePosition == null); activePosition = pos; if (sp.nodeType == NodeType.NonPV) { //enable call to search //search < NonPV, true > (pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode) } else if (sp.nodeType == NodeType.PV) { //enable call to search //search < PV, true > (pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode) } else if (sp.nodeType == NodeType.Root) { //enable call to search //search < Root, true > (pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode); } else { Debug.Assert(false); } Debug.Assert(searching); ThreadHelper.lock_grab(spinlock); searching = false; activePosition = null; ThreadHelper.lock_release(spinlock); sp.slavesMask &= ~(1UL << idx); //sp.slavesMask.reset(idx); sp.allSlavesSearching = false; sp.nodes += pos.nodes_searched(); // After releasing the lock we can't access any SplitPoint related data // in a safe way because it could have been released under our feet by // the sp master. ThreadHelper.lock_release(sp.spinLock); // Try to late join to another split point if none of its slaves has // already finished. SplitPoint bestSp = null; var minLevel = int.MaxValue; foreach (var th in ThreadPool.threads) { var size = th.splitPointsSize; // Local copy sp = size > 0 ? th.splitPoints[size - 1] : null; if (sp != null && sp.allSlavesSearching && Bitcount.popcount_Full(sp.slavesMask) < _.MAX_SLAVES_PER_SPLITPOINT && can_join(sp)) { Debug.Assert(this != th); Debug.Assert(!(this_sp != null && Bitcount.popcount_Full(sp.slavesMask) == 0)); Debug.Assert(ThreadPool.threads.Count > 2); // Prefer to join to SP with few parents to reduce the probability // that a cut-off occurs above us, and hence we waste our work. var level = 0; for (var p = th.activeSplitPoint; p != null; p = p.parentSplitPoint) { level++; } if (level < minLevel) { bestSp = sp; minLevel = level; } } } if (bestSp != null) { sp = bestSp; // Recheck the conditions under lock protection ThreadHelper.lock_grab(sp.spinLock); if (sp.allSlavesSearching && Bitcount.popcount_Full(sp.slavesMask) < _.MAX_SLAVES_PER_SPLITPOINT) { ThreadHelper.lock_grab(spinlock); if (can_join(sp)) { sp.slavesMask &= ~(1UL << idx); //sp->slavesMask.set(idx); activeSplitPoint = sp; searching = true; } ThreadHelper.lock_release(spinlock); } ThreadHelper.lock_release(sp.spinLock); } // If search is finished then sleep, otherwise just yield if (!ThreadPool.main().thinking) { Debug.Assert(this_sp == null); ThreadHelper.lock_grab(spinlock); while (!exit && !ThreadPool.main().thinking) ThreadHelper.cond_wait(sleepCondition, spinlock /*mutex*/); ThreadHelper.lock_release(spinlock); } else { System.Threading.Thread.Yield(); // Wait for a new job or for our slaves to finish } } } }
internal Thread(WaitHandle initEvent) : base(initEvent) { searching = false; maxPly = 0; splitPointsSize = 0; activeSplitPoint = null; activePosition = null; idx = ThreadPool.threads.Count; // Starts from 0 for (var j = 0; j < _.MAX_SPLITPOINTS_PER_THREAD; j++) { splitPoints[j] = new SplitPoint(); } }
private void PrepareTargetList(PathList path) { _splitPoint = null; foreach (Transform target in path.pathPoints) { _listOfTargets.Add(target); SplitPoint split = target.GetComponent<SplitPoint>(); if (split != null) { _splitPoint = split; //TODO this only accounts for two path choices. Vector3 midPosition = Vector3.zero; for (int i = 0; i < _splitPoint.newPaths.Length;i++) { midPosition += _splitPoint.newPaths[i].pathPoints[2].position; } CreateChoiceItem(); midPosition /= _splitPoint.newPaths.Length; _arrow = (Arrow)Instantiate(arrowPrefab,midPosition,Quaternion.identity); _arrow.transform.parent = _splitPoint.transform; _choiceIsReady = true; Transform nextPath = GetNextPathChoice(); _arrow.SetTarget(nextPath); SetLabelsActive(nextPath); scoreManager.AddRound(); } } }