internal static void Warmup() { // Bench 128 4 17 yields: // CheckInfoBroker: 140 (4/32/40/32/32) // EvalInfoBroker: 16 (4/4/4/4) // SwapListBroker: 16 (4/4/4/4) // MovesSearchedBroker: 120 (28/36/28/28) // PositionBroker: 32 (8/8/8/8) // StateInfoArrayBroker: 16 (4/4/4/4) // MListBroker: 20 (4/4/4/4/4) // LoopStackBroker: 32 (8/8/8/8) // MovePickerBroker: 136 (32/40/32/32) // StateInfoBroker: 132 (32/36/32/32) // Specific allocation not to overallocate memory for nothing int i, brokerSize; // Reusing brokers brokerSize = 40; for (i = 0; i < brokerSize; i++) { CheckInfoBroker.GetObject(); } for (i = 0; i < brokerSize; i++) { CheckInfoBroker.Free(); } brokerSize = 4; for (i = 0; i < brokerSize; i++) { EvalInfoBroker.GetObject(); } for (i = 0; i < brokerSize; i++) { EvalInfoBroker.Free(); } brokerSize = 4; for (i = 0; i < brokerSize; i++) { SwapListBroker.GetObject(); } for (i = 0; i < brokerSize; i++) { SwapListBroker.Free(); } brokerSize = 36; for (i = 0; i < brokerSize; i++) { MovesSearchedBroker.GetObject(); } for (i = 0; i < brokerSize; i++) { MovesSearchedBroker.Free(); } brokerSize = 8; for (i = 0; i < brokerSize; i++) { PositionBroker.GetObject(); } for (i = 0; i < brokerSize; i++) { PositionBroker.Free(); } brokerSize = 4; for (i = 0; i < brokerSize; i++) { StateInfoArrayBroker.GetObject(); } for (i = 0; i < brokerSize; i++) { StateInfoArrayBroker.Free(); } brokerSize = 4; for (i = 0; i < brokerSize; i++) { MListBroker.GetObject(); } for (i = 0; i < brokerSize; i++) { MListBroker.Free(); } brokerSize = 36; for (i = 0; i < brokerSize; i++) { StateInfoBroker.GetObject(); } for (i = 0; i < brokerSize; i++) { StateInfoBroker.Free(); } // Recycling brokers brokerSize = 8; LoopStack[] arrLoopStack = new LoopStack[brokerSize]; for (i = 0; i < brokerSize; i++) { arrLoopStack[i] = LoopStackBroker.GetObject(); } for (i = brokerSize - 1; i >= 0; i--) { LoopStackBroker.Free(arrLoopStack[i]); } brokerSize = 40; MovePicker[] arrMovePicker = new MovePicker[brokerSize]; for (i = 0; i < brokerSize; i++) { arrMovePicker[i] = MovePickerBroker.GetObject(); } for (i = brokerSize - 1; i >= 0; i--) { MovePickerBroker.Free(arrMovePicker[i]); } }
internal static void init() { for (int i = 0; i < Constants.BROKER_SLOTS; i++) { _pool[i] = new MovePicker[0]; } }
internal static void Free(MovePicker obj) { obj.Recycle(); #if WINDOWS_RT _cnt[Environment.CurrentManagedThreadId & Constants.BROKER_SLOT_MASK]--; #else _cnt[System.Threading.Thread.CurrentThread.ManagedThreadId & Constants.BROKER_SLOT_MASK]--; #endif }
internal static MovePicker GetObject() { int slotID = System.Threading.Thread.CurrentThread.ManagedThreadId & Constants.BROKER_SLOT_MASK; if (_cnt[slotID] == _pool[slotID].Length) { int poolLength = _pool[slotID].Length; MovePicker[] temp = new MovePicker[poolLength + Constants.BrokerCapacity]; Array.Copy(_pool[slotID], temp, poolLength); for (int i = 0; i < Constants.BrokerCapacity; i++) { temp[poolLength + i] = new MovePicker(); } _pool[slotID] = temp; } return(_pool[slotID][_cnt[slotID]++]); }
/// Constructors of the MovePicker class. As arguments we pass information /// to help it to return the presumably good moves first, to decide which /// moves to return (in the quiescence search, for instance, we only want to /// search captures, promotions and some checks) and about how important good /// move ordering is at the current node. internal void MovePickerC(Position p, Move ttm, Depth d, History h, Stack ss, Value beta, MovePicker mpExt) { pos = p; H = h; depth = d; mpExternal = mpExt; Debug.Assert(d > DepthC.DEPTH_ZERO); captureThreshold = 0; curMovePos = lastMovePos = 0; lastBadCapturePos = Constants.MAX_MOVES - 1; recaptureSquare = 0; lastQuietPos = 0; mpos = 0; if (p.in_check()) { phase = SequencerC.EVASION; } else { phase = SequencerC.MAIN_SEARCH; ms[Constants.MAX_MOVES].move = ss.killers0; ms[Constants.MAX_MOVES + 1].move = ss.killers1; // Consider sligtly negative captures as good if at low depth and far from beta if (ss.eval < beta - Constants.PawnValueMidgame && d < 3 * DepthC.ONE_PLY) { captureThreshold = -Constants.PawnValueMidgame; } // Consider negative captures as good if still enough to reach beta else if (ss.eval > beta) { captureThreshold = beta - ss.eval; } } ttMove = (ttm != 0 && pos.is_pseudo_legal(ttm) ? ttm : MoveC.MOVE_NONE); lastMovePos += ((ttMove != MoveC.MOVE_NONE) ? 1 : 0); }
internal static void Free(MovePicker obj) { obj.Recycle(); _cnt[System.Threading.Thread.CurrentThread.ManagedThreadId & Constants.BROKER_SLOT_MASK]--; }
internal static MovePicker GetObject() { int slotID = System.Threading.Thread.CurrentThread.ManagedThreadId & Constants.BROKER_SLOT_MASK; if (_cnt[slotID] == _pool[slotID].Length) { int poolLength = _pool[slotID].Length; MovePicker[] temp = new MovePicker[poolLength + Constants.BrokerCapacity]; Array.Copy(_pool[slotID], temp, poolLength); for (int i = 0; i < Constants.BrokerCapacity; i++) { temp[poolLength + i] = new MovePicker(); } _pool[slotID] = temp; } return _pool[slotID][_cnt[slotID]++]; }
// 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, or because we have no unused split // point objects), 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 told 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 static Value split(bool Fake, Position pos, Stack[] ss, int ssPos, Value alpha, Value beta, Value bestValue, ref Move bestMove, Depth depth, Move threatMove, int moveCount, MovePicker mp, int nodeType) { Debug.Assert(pos.pos_is_ok()); Debug.Assert(bestValue > -ValueC.VALUE_INFINITE); Debug.Assert(bestValue <= alpha); Debug.Assert(alpha < beta); Debug.Assert(beta <= ValueC.VALUE_INFINITE); Debug.Assert(depth > DepthC.DEPTH_ZERO); Thread master = pos.this_thread(); if (master.splitPointsCnt >= Constants.MAX_SPLITPOINTS_PER_THREAD) { return(bestValue); } // Pick the next available split point from the split point stack SplitPoint sp = master.splitPoints[master.splitPointsCnt]; sp.parent = master.curSplitPoint; sp.master = master; sp.cutoff = false; sp.slavesMask = 1UL << master.idx; #if ACTIVE_REPARENT sp.allSlavesRunning = true; #endif sp.depth = depth; sp.bestMove = bestMove; sp.threatMove = threatMove; sp.alpha = alpha; sp.beta = beta; sp.nodeType = nodeType; sp.bestValue = bestValue; sp.mp = mp; sp.moveCount = moveCount; sp.pos = pos; sp.nodes = 0; sp.ss = ss; sp.ssPos = ssPos; Debug.Assert(master.is_searching); master.curSplitPoint = sp; int slavesCnt = 0; ThreadHelper.lock_grab(sp.Lock); ThreadHelper.lock_grab(splitLock); for (int i = 0; i < size() && !Fake; ++i) { if (threads[i].is_available_to(master)) { sp.slavesMask |= 1UL << i; threads[i].curSplitPoint = sp; threads[i].is_searching = true; // Slave leaves idle_loop() if (useSleepingThreads) { threads[i].wake_up(); } if (++slavesCnt + 1 >= maxThreadsPerSplitPoint) // Master is always included { break; } } } master.splitPointsCnt++; ThreadHelper.lock_release(splitLock); ThreadHelper.lock_release(sp.Lock); // Everything is set up. The master thread enters the idle loop, from which // it will instantly launch a search, because its is_searching flag is set. // We pass the split point as a parameter to the idle loop, which means that // the thread will return from the idle loop when all slaves have finished // their work at this split point. if (slavesCnt != 0 || Fake) { master.idle_loop(sp, null); // In helpful master concept a master can help only a sub-tree of its split // point, and because here is all finished is not possible master is booked. Debug.Assert(!master.is_searching); } // We have returned from the idle loop, which means that all threads are // finished. Note that setting is_searching and decreasing activeSplitPoints is // done under lock protection to avoid a race with Thread::is_available_to(). ThreadHelper.lock_grab(sp.Lock); // To protect sp->nodes ThreadHelper.lock_grab(splitLock); master.is_searching = true; master.splitPointsCnt--; master.curSplitPoint = sp.parent; pos.nodes += sp.nodes; bestMove = sp.bestMove; ThreadHelper.lock_release(splitLock); ThreadHelper.lock_release(sp.Lock); return(sp.bestValue); }
public void Recycle() { pos = null; H = null; mpExternal = null; }
// 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, or because we have no unused split // point objects), 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 told 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 static Value split(bool Fake, Position pos, Stack[] ss, int ssPos, Value alpha, Value beta, Value bestValue, ref Move bestMove, Depth depth, Move threatMove, int moveCount, MovePicker mp, int nodeType) { Debug.Assert(pos.pos_is_ok()); Debug.Assert(bestValue > -ValueC.VALUE_INFINITE); Debug.Assert(bestValue <= alpha); Debug.Assert(alpha < beta); Debug.Assert(beta <= ValueC.VALUE_INFINITE); Debug.Assert(depth > DepthC.DEPTH_ZERO); Thread master = pos.this_thread(); if (master.splitPointsCnt >= Constants.MAX_SPLITPOINTS_PER_THREAD) return bestValue; // Pick the next available split point from the split point stack SplitPoint sp = master.splitPoints[master.splitPointsCnt]; sp.parent = master.curSplitPoint; sp.master = master; sp.cutoff = false; sp.slavesMask = 1UL << master.idx; #if ACTIVE_REPARENT sp.allSlavesRunning = true; #endif sp.depth = depth; sp.bestMove = bestMove; sp.threatMove = threatMove; sp.alpha = alpha; sp.beta = beta; sp.nodeType = nodeType; sp.bestValue = bestValue; sp.mp = mp; sp.moveCount = moveCount; sp.pos = pos; sp.nodes = 0; sp.ss = ss; sp.ssPos = ssPos; Debug.Assert(master.is_searching); master.curSplitPoint = sp; int slavesCnt = 0; ThreadHelper.lock_grab(sp.Lock); ThreadHelper.lock_grab(splitLock); for (int i = 0; i < size() && !Fake; ++i) if (threads[i].is_available_to(master)) { sp.slavesMask |= 1UL << i; threads[i].curSplitPoint = sp; threads[i].is_searching = true; // Slave leaves idle_loop() if (useSleepingThreads) threads[i].wake_up(); if (++slavesCnt + 1 >= maxThreadsPerSplitPoint) // Master is always included break; } master.splitPointsCnt++; ThreadHelper.lock_release(splitLock); ThreadHelper.lock_release(sp.Lock); // Everything is set up. The master thread enters the idle loop, from which // it will instantly launch a search, because its is_searching flag is set. // We pass the split point as a parameter to the idle loop, which means that // the thread will return from the idle loop when all slaves have finished // their work at this split point. if (slavesCnt != 0 || Fake) { master.idle_loop(sp, null); // In helpful master concept a master can help only a sub-tree of its split // point, and because here is all finished is not possible master is booked. Debug.Assert(!master.is_searching); } // We have returned from the idle loop, which means that all threads are // finished. Note that setting is_searching and decreasing activeSplitPoints is // done under lock protection to avoid a race with Thread::is_available_to(). ThreadHelper.lock_grab(sp.Lock); // To protect sp->nodes ThreadHelper.lock_grab(splitLock); master.is_searching = true; master.splitPointsCnt--; master.curSplitPoint = sp.parent; pos.nodes += sp.nodes; bestMove = sp.bestMove; ThreadHelper.lock_release(splitLock); ThreadHelper.lock_release(sp.Lock); return sp.bestValue; }
/// Constructors of the MovePicker class. As arguments we pass information /// to help it to return the presumably good moves first, to decide which /// moves to return (in the quiescence search, for instance, we only want to /// search captures, promotions and some checks) and about how important good /// move ordering is at the current node. internal void MovePickerC(Position p, Move ttm, Depth d, History h, Stack ss, Value beta, MovePicker mpExt) { pos = p; H = h; depth = d; mpExternal = mpExt; Debug.Assert(d > DepthC.DEPTH_ZERO); captureThreshold = 0; curMovePos = lastMovePos = 0; lastBadCapturePos = Constants.MAX_MOVES - 1; recaptureSquare = 0; lastQuietPos = 0; mpos = 0; if (p.in_check()) { phase = SequencerC.EVASION; } else { phase = SequencerC.MAIN_SEARCH; ms[Constants.MAX_MOVES].move = ss.killers0; ms[Constants.MAX_MOVES + 1].move = ss.killers1; // Consider sligtly negative captures as good if at low depth and far from beta if (ss.eval < beta - Constants.PawnValueMidgame && d < 3 * DepthC.ONE_PLY) captureThreshold = -Constants.PawnValueMidgame; // Consider negative captures as good if still enough to reach beta else if (ss.eval > beta) captureThreshold = beta - ss.eval; } ttMove = (ttm != 0 && pos.is_pseudo_legal(ttm) ? ttm : MoveC.MOVE_NONE); lastMovePos += ((ttMove != MoveC.MOVE_NONE) ? 1 : 0); }
public void Recycle() { this.pos = null; this.H = null; this.mpExternal = null; }
// 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 // told 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 static void split( bool Fake, Position pos, Stack[] ss, int ssPos, int alpha, int beta, ref int bestValue, ref int bestMove, int depth, int threatMove, int moveCount, MovePicker movePicker, int nodeType) { Debug.Assert(bestValue <= alpha && alpha < beta && beta <= ValueC.VALUE_INFINITE); Debug.Assert(pos.pos_is_ok()); Debug.Assert(bestValue > -ValueC.VALUE_INFINITE); Debug.Assert(depth > DepthC.DEPTH_ZERO); var thisThread = pos.this_thread(); Debug.Assert(thisThread.searching); Debug.Assert(thisThread.splitPointsSize < Constants.MAX_SPLITPOINTS_PER_THREAD); // Pick the next available split point from the split point stack var sp = thisThread.splitPoints[thisThread.splitPointsSize]; sp.parentSplitPoint = thisThread.activeSplitPoint; sp.master = thisThread; sp.cutoff = false; sp.slavesMask = 1UL << thisThread.idx; #if ACTIVE_REPARENT sp.allSlavesRunning = true; #endif sp.depth = depth; sp.bestMove = bestMove; sp.threatMove = threatMove; sp.alpha = alpha; sp.beta = beta; sp.nodeType = nodeType; sp.bestValue = bestValue; sp.movePicker = movePicker; sp.moveCount = moveCount; sp.pos = pos; sp.nodes = 0; sp.ss = ss; sp.ssPos = ssPos; // Try to allocate available threads and ask them to start searching setting // 'searching' flag. This must be done under lock protection to avoid concurrent // allocation of the same slave by another master. ThreadHelper.lock_grab(splitLock); ThreadHelper.lock_grab(sp.Lock); thisThread.splitPointsSize++; thisThread.activeSplitPoint = sp; thisThread.activePosition = null; var slavesCnt = 1; // Master is always included Thread slave; while ((slave = Threads.available_slave(thisThread)) != null && ++slavesCnt <= Threads.maxThreadsPerSplitPoint && !Fake) { sp.slavesMask |= 1UL << slave.idx; slave.activeSplitPoint = sp; slave.searching = true; // Slave leaves idle_loop() slave.notify_one(); // Could be sleeping } ThreadHelper.lock_release(sp.Lock); ThreadHelper.lock_release(splitLock); // 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. // We pass the split point as a parameter to the idle loop, which means that // the thread will return from the idle loop when all slaves have finished // their work at this split point. if (slavesCnt > 1 || Fake) { thisThread.base_idle_loop(null); // Force a call to base class idle_loop() // In helpful master concept a master can help only a sub-tree of its split // point, and because here is all finished is not possible master is booked. Debug.Assert(!thisThread.searching); Debug.Assert(thisThread.activePosition == null); } // We have returned from the idle loop, which means that all threads are // finished. Note that setting searching and decreasing activeSplitPoints is // done under lock protection to avoid a race with Thread::is_available_to(). ThreadHelper.lock_grab(splitLock); ThreadHelper.lock_grab(sp.Lock); // To protect sp->nodes thisThread.searching = true; thisThread.splitPointsSize--; thisThread.activeSplitPoint = sp.parentSplitPoint; thisThread.activePosition = pos; pos.nodes += sp.nodes; bestMove = sp.bestMove; bestValue = sp.bestValue; ThreadHelper.lock_release(sp.Lock); ThreadHelper.lock_release(splitLock); }