- /* 
-     Texel - A UCI chess engine. 
-     Copyright (C) 2013-2014  Peter Ă–sterlund, peterosterlund2@gmail.com 
-   
-     This program is free software: you can redistribute it and/or modify 
-     it under the terms of the GNU General Public License as published by 
-     the Free Software Foundation, either version 3 of the License, or 
-     (at your option) any later version. 
-   
-     This program is distributed in the hope that it will be useful, 
-     but WITHOUT ANY WARRANTY; without even the implied warranty of 
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
-     GNU General Public License for more details. 
-   
-     You should have received a copy of the GNU General Public License 
-     along with this program.  If not, see <http://www.gnu.org/licenses/>. 
- */ 
-   
- /* 
-  * parallel.hpp 
-  * 
-  *  Created on: Jul 9, 2013 
-  *      Author: petero 
-  */ 
-   
- #ifndef PARALLEL_HPP_ 
- #define PARALLEL_HPP_ 
-   
- #include "killerTable.hpp" 
- #include "history.hpp" 
- #include "transpositionTable.hpp" 
- #include "evaluate.hpp" 
- #include "searchUtil.hpp" 
- #include "constants.hpp" 
- #include "util/timeUtil.hpp" 
- #include "util/heap.hpp" 
-   
- #include <memory> 
- #include <vector> 
- #include <set> 
- #include <thread> 
- #include <mutex> 
- #include <condition_variable> 
- #include <atomic> 
-   
-   
- class Search; 
- class SearchTreeInfo; 
-   
- class ParallelData; 
- class SplitPoint; 
- class SplitPointMove; 
- class FailHighInfo; 
- class DepthNpsInfo; 
-   
-   
- class WorkerThread { 
- public: 
-     /** Constructor. Does not start new thread. */ 
-     WorkerThread(int threadNo, ParallelData& pd, TranspositionTable& tt); 
-   
-     /** Destructor. Waits for thread to terminate. */ 
-     ~WorkerThread(); 
-   
-     WorkerThread(const WorkerThread&) = delete; 
-     WorkerThread& operator=(const WorkerThread&) = delete; 
-   
-     /** Start thread. */ 
-     void start(); 
-   
-     /** Wait for thread to stop. */ 
-     void join(); 
-   
-     /** Return true if thread is running. */ 
-     bool threadRunning() const; 
-   
-     /** Return thread number. The first worker thread is number 1. */ 
-     int getThreadNo() const; 
-   
-     /** For debugging. */ 
-     double getPUseful() const; 
-   
- private: 
-     /** Thread main loop. */ 
-     void mainLoop(int minProbeDepth); 
-   
-     int threadNo; 
-     ParallelData& pd; 
-     std::shared_ptr<std::thread> thread; 
-   
-     std::shared_ptr<Evaluate::EvalHashTables> et; 
-     std::shared_ptr<KillerTable> kt; 
-     std::shared_ptr<History> ht; 
-     TranspositionTable& tt; 
-   
-     double pUseful; // Probability that thread is currently doing something useful, for debugging 
- }; 
-   
-   
- /** Priority queue of pending search tasks. Handles thread safety. */ 
- class WorkQueue { 
-     friend class ParallelTest; 
- public: 
-     /** Constructor. */ 
-     WorkQueue(const FailHighInfo& fhInfo, const DepthNpsInfo& npsInfo); 
-   
-     /** Set/get stopped flag. */ 
-     void setStopped(bool stop); 
-     bool isStopped() const; 
-   
-     /** Reset dynamic minimum split depth to default value. */ 
-     void resetSplitDepth(); 
-   
-     /** Add SplitPoint to work queue. */ 
-     void addWork(const std::shared_ptr<SplitPoint>& sp); 
-   
-     /** Try to add SplitPoint to work queue. If queue is contended add SplitPoint 
-      * to pending instead. */ 
-     void tryAddWork(const std::shared_ptr<SplitPoint>& sp, 
-                     std::vector<std::shared_ptr<SplitPoint>>& pending); 
-   
-     /** Get best move for helper thread to work on. */ 
-     std::shared_ptr<SplitPoint> getWork(int& moveNo, ParallelData& pd, int threadNo); 
-     std::shared_ptr<SplitPoint> getWork(int& moveNo, ParallelData& pd, int threadNo, 
-                                         int& prio, double& pUseful); 
-   
-     /** A helper thread stopped working on a move before it was finished. */ 
-     void returnMove(const std::shared_ptr<SplitPoint>& sp, int moveNo); 
-   
-     /** Set which move number the SplitPoint owner is currently searching. 
-      * @return Score from helper search thread. UNKNOWN_SCORE if no helper has 
-      *         searched the move. */ 
-     int setOwnerCurrMove(const std::shared_ptr<SplitPoint>& sp, int moveNo, int alpha); 
-   
-     /** Cancel this SplitPoint and all children. */ 
-     void cancel(const std::shared_ptr<SplitPoint>& sp); 
-   
-     /** Set move to canceled after helper thread finished searching it. */ 
-     void moveFinished(const std::shared_ptr<SplitPoint>& sp, int moveNo, 
-                       bool cancelRemaining, int score); 
-   
-     /** Return probability that the best unstarted move needs to be searched. 
-      *  Also return the corresponding SplitPoint. */ 
-     double getBestProbability(std::shared_ptr<SplitPoint>& bestSp) const; 
-     double getBestProbability() const; 
-     int getBestPrio() const; 
-   
-     /** Return current dynamic minimum split depth. */ 
-     int getMinSplitDepth() const; 
-   
-     /** For performance measurements on queue operations. */ 
-     void resetStat(); 
-     TimeSampleStatistics& getAddWorkStat(int th); 
-     TimeSampleStatistics& getGetWorkStat(int th); 
-     void printStats(std::ostream& os, int nThreads); 
-   
- private: 
-     /** Insert sp in queue. Notify condition variable if queue becomes non-empty. */ 
-     void insertInQueue(const std::shared_ptr<SplitPoint>& sp); 
-   
-     /** Recompute probabilities for "sp" and all children. Update "queue" and "waiting". */ 
-     void updateProbabilities(const std::shared_ptr<SplitPoint>& sp); 
-   
-     /** Cancel "sp" and all children. Assumes mutex already locked. */ 
-     void cancelInternal(const std::shared_ptr<SplitPoint>& sp); 
-   
-     void printSpTree(std::ostream& os, const ParallelData& pd, 
-                      int threadNo, const std::shared_ptr<SplitPoint> selectedSp, 
-                      int selectedMove); 
-     void findLeaves(const std::shared_ptr<SplitPoint>& sp, std::vector<int>& parentThreads, 
-                     std::vector<std::shared_ptr<SplitPoint>>& leaves); 
-   
-     /** Helper for addWork() and tryAddWork(). */ 
-     void addWorkInternal(const std::shared_ptr<SplitPoint>& sp); 
-   
-     /** Scoped lock that measures lock contention and adjusts minSplitDepth accordingly. */ 
-     class Lock { 
-     public: 
-         Lock(const WorkQueue* wq0); 
-         void wait(std::condition_variable& cv); 
-     private: 
-         const WorkQueue& wq; 
-         std::unique_lock<std::mutex> lock; 
-     }; 
-     friend class Lock; 
-     std::atomic<bool> stopped; 
-   
-     mutable int minSplitDepth;      // Dynamic minimum split depth 
-     mutable U64 nContended;         // Number of times mutex has been contended 
-     mutable U64 nNonContended;      // Number of times mutex has not been contended 
-   
-     std::condition_variable cv;     // Notified when wq becomes non-empty and when search should stop 
-     const FailHighInfo& fhInfo; 
-     const DepthNpsInfo& npsInfo; 
-     mutable std::mutex mutex; 
-   
-     // SplitPoints with unstarted SplitPointMoves 
-     // SplitPoints with no unstarted moves have negative priority. 
-     Heap<SplitPoint> queue; 
-   
-     // For performance debugging 
-     static const int maxStatThreads = 64; 
-     TimeSampleStatisticsVector<maxStatThreads*2> wqStat; 
- }; 
-   
-   
- /** Fail high statistics, for estimating SplitPoint usefulness probabilities. */ 
- class FailHighInfo { 
- public: 
-     /** Constructor. */ 
-     FailHighInfo(); 
-   
-     /** Return probability that move moveNo needs to be searched. 
-      * @param parentMoveNo  Move number of move leading to this position. 
-      * @param currMoveNo    Move currently being searched. 
-      * @param moveNo        Move number to get probability for. 
-      * @param allNode       True if this is an expected ALL node. */ 
-     double getMoveNeededProbability(int parentMoveNo, int currMoveNo, int moveNo, bool allNode) const; 
-   
-     /** Return probability that move moveNo needs to be searched in a PV node. 
-      * @param currMoveNo    Move currently being searched. 
-      * @param moveNo        Move number to get probability for. 
-      * @param allNode       True if this is an expected ALL node. */ 
-     double getMoveNeededProbabilityPv(int currMoveNo, int moveNo) const; 
-   
-     /** Add fail high information. 
-      * @param parentMoveNo  Move number of move leading to this position. 
-      * @param nSearched     Number of moves searched at this node. 
-      * @param failHigh      True if the node failed high. 
-      * @param allNode       True if this is an expected ALL node. */ 
-     void addData(int parentMoveNo, int nSearched, bool failHigh, bool allNode); 
-   
-     /** Add "alpha increased" information for PV nodes. 
-      * @param  nSearched     Number of moves searched at this node. 
-      * @param  alphaChanged  True if alpha increased after searching move. */ 
-     void addPvData(int nSearched, bool alphaChanged); 
-   
-     /** Rescale the counters so that future updates have more weight. */ 
-     void reScale(); 
-   
-     /** Print object state to "os", for debugging. */ 
-     void print(std::ostream& os) const; 
-   
- private: 
-     void reScaleInternal(int factor); 
-     void reScalePv(int factor); 
-   
-     int getNodeType(int moveNo, bool allNode) const; 
-   
-     mutable std::mutex mutex; 
-   
-     static const int NUM_NODE_TYPES = 4; 
-     static const int NUM_STAT_MOVES = 15; 
-   
-     RangeSumArray<NUM_STAT_MOVES> failHiCount[NUM_NODE_TYPES]; // [parentMoveNo>0?1:0][moveNo] 
-     std::atomic<int> failLoCount[NUM_NODE_TYPES];              // [parentMoveNo>0?1:0] 
-     std::atomic<int> totCount;                                 // Sum of all counts 
-   
-     std::atomic<int> newAlpha[NUM_STAT_MOVES]; 
-     std::atomic<int> totPvCount; 
- }; 
-   
- class DepthNpsInfo { 
- public: 
-     /** Constructor. */ 
-     DepthNpsInfo(); 
-   
-     /** Reset */ 
-     void reset(); 
-   
-     /** Set thread 0 estimated nps. Used to scale efficiency calculations 
-      *  to [0,1] interval. */ 
-     void setBaseNps(double nps); 
-   
-     /** Add one data point. */ 
-     void addData(int depth, U64 nodes, double waitTime, double searchTime); 
-   
-     /** Return estimate search efficiency when searching to a given depth. */ 
-     double efficiency(int depth) const; 
-   
-     /** Print object state to "os", for debugging. */ 
-     void print(std::ostream& os, int iterDepth); 
-   
-     /** Maximum depth statistics is kept for. */ 
-     static const int maxDepth = 48; 
-   
- private: 
-     /** Helper method for efficiency() and print(). */ 
-     double efficiencyInternal(int depth) const; 
-   
-     mutable std::mutex mutex; 
-   
-     struct NpsData { 
-         NpsData(); 
-         U32 nSearches; 
-         U64 nodes; 
-         double time; 
-     }; 
-     NpsData npsData[maxDepth]; 
-     double nps0; 
-     U32 nSearches;      // Total number of searches 
-     double waitTime;    // Total waiting time 
- }; 
-   
- /** Top-level parallel search data structure. */ 
- class ParallelData { 
- public: 
-     /** Constructor. */ 
-     ParallelData(TranspositionTable& tt); 
-   
-     /** Create/delete worker threads so that there are numWorkers in total. */ 
-     void addRemoveWorkers(int numWorkers); 
-   
-     /** Start all worker threads. */ 
-     void startAll(); 
-   
-     /** Stop all worker threads. */ 
-     void stopAll(); 
-   
-   
-     /** Return number of helper threads in use. */ 
-     int numHelperThreads() const; 
-   
-     /** Get number of nodes searched by all helper threads. */ 
-     S64 getNumSearchedNodes() const; 
-   
-     /** Get number of TB hits for all helper threads. */ 
-     S64 getTbHits() const; 
-   
-     /** Add nNodes to total number of searched nodes. */ 
-     void addSearchedNodes(S64 nNodes); 
-   
-     /** Add nTbHits to number of TB hits. */ 
-     void addTbHits(S64 nTbHits); 
-   
-   
-     /** For debugging. */ 
-     const WorkerThread& getHelperThread(int i) const; 
-   
-     FailHighInfo fhInfo; 
-     DepthNpsInfo npsInfo; 
-   
-     WorkQueue wq; 
-   
-     /** Move played in Search::iterativeDeepening before calling negaScout. For debugging. */ 
-     Move topMove; 
-   
-     /** Current node index in thread 0. Used by tree logging code. */ 
-     std::atomic<U32> t0Index; 
-   
- private: 
-     /** Vector of helper threads. Master thread not included. */ 
-     std::vector<std::shared_ptr<WorkerThread>> threads; 
-   
-     TranspositionTable& tt; 
-   
-     std::atomic<S64> totalHelperNodes; // Number of nodes searched by all helper threads 
-     std::atomic<S64> helperTbHits;     // Number of TB hits for all helper threads 
- }; 
-   
-   
- /** SplitPoint does not handle thread safety, WorkQueue must do that.  */ 
- class SplitPoint : public Heap<SplitPoint>::HeapObject { 
-     friend class ParallelTest; 
- public: 
-     /** Constructor. */ 
-     SplitPoint(int threadNo, 
-                const std::shared_ptr<SplitPoint>& parentSp, int parentMoveNo, 
-                const Position& pos, const std::vector<U64>& posHashList, 
-                int posHashListSize, const SearchTreeInfo& sti, 
-                const KillerTable& kt, const History& ht, 
-                int alpha, int beta, int ply, int depth); 
-   
-     SplitPoint(const SplitPoint&) = delete; 
-     SplitPoint& operator=(const SplitPoint&) = delete; 
-   
-     /** Add a child SplitPoint */ 
-     void addChild(const std::weak_ptr<SplitPoint>& child); 
-   
-     /** Add a move to the SplitPoint. */ 
-     void addMove(int moveNo, const SplitPointMove& spMove); 
-   
-     /** Assign sequence number. */ 
-     void setSeqNo(); 
-   
-     /** compute pSpUseful and pnextMoveUseful. */ 
-     void computeProbabilities(const FailHighInfo& fhInfo, const DepthNpsInfo& npsInfo); 
-   
-     /** Get parent SplitPoint, or null for root SplitPoint. */ 
-     std::shared_ptr<SplitPoint> getParent() const; 
-   
-     /** Get children SplitPoints. */ 
-     const std::vector<std::weak_ptr<SplitPoint>>& getChildren() const; 
-   
-   
-     U64 getSeqNo() const; 
-     double getPSpUseful() const; 
-     double getPNextMoveUseful() const; 
-     const History& getHistory() const; 
-     const KillerTable& getKillerTable() const; 
-     const SplitPointMove& getSpMove(int moveNo) const; 
-   
-     /** Return probability that moveNo needs to be searched. */ 
-     double getPMoveUseful(const FailHighInfo& fhInfo, int moveNo) const; 
-   
-     void getPos(Position& pos, const Move& move) const; 
-     void getPosHashList(const Position& pos, std::vector<U64>& posHashList, 
-                         int& posHashListSize) const; 
-     const SearchTreeInfo& getSearchTreeInfo() const; 
-     int getAlpha() const; 
-     int getBeta() const; 
-     int getPly() const; 
-     int getDepth() const; 
-   
-   
-     /** Get index of first unstarted move. Mark move as being searched. 
-      * Return -1 if there are no unstarted moves. */ 
-     int getNextMove(const FailHighInfo& fhInfo); 
-   
-     /** A helper thread stopped working on a move before it was finished. */ 
-     void returnMove(int moveNo); 
-   
-     /** Set which move number the SplitPoint owner is currently searching. 
-      * @return Score from helper search thread. UNKNOWN_SCORE if no helper has 
-      *         searched the move. */ 
-     int setOwnerCurrMove(int moveNo, int alpha); 
-   
-     /** Cancel this SplitPoint and all children. */ 
-     void cancel(); 
-   
-     /** Return true if this SplitPoint has been canceled. */ 
-     bool isCanceled() const; 
-   
-     /** Set move to canceled after helper thread finished searching it. */ 
-     void moveFinished(int moveNo, bool cancelRemaining, int score); 
-   
-     /** Return true if there are moves that have not been started to be searched. */ 
-     bool hasUnStartedMove() const; 
-   
-     /** Return true if there are moves that have not been finished (canceled) yet. */ 
-     bool hasUnFinishedMove() const; 
-   
-     /** Return true if this SplitPoint is an ancestor to "sp". */ 
-     bool isAncestorTo(const SplitPoint& sp) const; 
-   
-     /** Return true if some other thread is helping the SplitPoint owner. */ 
-     bool hasHelperThread() const; 
-   
-     /** Return true if the held SplitPoint is an estimated ALL node. */ 
-     bool isAllNode() const; 
-   
-     /** Return true if beta > alpha + 1. */ 
-     bool isPvNode() const; 
-   
-     /** Compute SplitPoint priority. The SplitPoint with highest 
-      * priority will be selected first by the next available thread. */ 
-     int getSpPrio(const DepthNpsInfo& npsInfo) const; 
-   
-     /** Thread that created this SplitPoint. */ 
-     int owningThread() const; 
-   
-     /** Return true if object is or has been inserted in WorkQueue. */ 
-     bool wasInserted() const; 
-   
-     /** Mark SplitPoint as inserted in WorkQueue. */ 
-     void setInserted(); 
-   
-     /** Print object state to "os", for debugging. */ 
-     void print(std::ostream& os, int level, const FailHighInfo& fhInfo) const; 
-   
-     /** For debugging. */ 
-     int getParentMoveNo() const; 
-   
-     /** For debugging. */ 
-     int getCurrMoveNo() const; 
-   
-     /** Get index of first unstarted move, or -1 if there is no unstarted move. */ 
-     int findNextMove(const FailHighInfo& fhInfo) const; 
-   
- private: 
-     /** Return probability that moveNo needs to be searched, by calling corresponding 
-      * function in fhInfo. */ 
-     double getMoveNeededProbability(const FailHighInfo& fhInfo, int moveNo) const; 
-   
-     /** Remove null entries from children vector. */ 
-     void cleanUpChildren(); 
-   
-     const Position pos; 
-     const std::vector<U64> posHashList; // List of hashes for previous positions up to the last "zeroing" move. 
-     const int posHashListSize; 
-     const SearchTreeInfo searchTreeInfo;   // For ply-1 
-     const KillerTable& kt; 
-     const History& ht; 
-   
-     RelaxedShared<int> alpha; 
-     const int beta; 
-     const int ply; 
-     const int depth; 
-     const bool isPV; 
-   
-     double pSpUseful;       // Probability that this SplitPoint is needed. 100% if parent is null. 
-     double pNextMoveUseful; // Probability that next unstarted move needs to be searched. 
-   
-     const int threadNo;     // Owning thread 
-     const std::shared_ptr<SplitPoint> parent; 
-     const int parentMoveNo; // Move number in parent SplitPoint that generated this SplitPoint 
-     std::vector<std::weak_ptr<SplitPoint>> children; 
-   
-     static U64 nextSeqNo; 
-     U64 seqNo;      // To break ties when two objects have same priority. Set by addWork() under lock 
-     int currMoveNo; 
-     std::vector<SplitPointMove> spMoves; 
-   
-     bool inserted; // True if sp has been inserted in WorkQueue. Remains true after deletion. 
-     bool canceled; 
- }; 
-   
-   
- /** Represents one move at a SplitPoint. */ 
- class SplitPointMove { 
- public: 
-     /** Constructor */ 
-     SplitPointMove(const Move& move, int lmr, int depth, 
-                    int captSquare, bool inCheck); 
-   
-     const Move& getMove() const; 
-     int getLMR() const; 
-     int getDepth() const; 
-     int getRecaptureSquare() const; 
-     bool getInCheck() const; 
-   
-     bool isCanceled() const; 
-     void setCanceled(bool value); 
-   
-     bool isSearching() const; 
-     void setSearching(bool value); 
-   
-     void setScore(int s); 
-     int getScore() const; 
-   
- private: 
-     Move move;      // Position defined by sp->pos + move 
-     int lmr;        // Amount of LMR reduction 
-     int depth; 
-     int captSquare; // Recapture square, or -1 if no recapture 
-     bool inCheck;   // True if side to move is in check 
-   
-     RelaxedShared<bool> canceled; // Result is no longer needed 
-     bool searching; // True if currently searched by a helper thread 
-     int score; 
- }; 
-   
-   
- /** Handle a SplitPoint object using RAII. */ 
- class SplitPointHolder { 
- public: 
-     /** Constructor. */ 
-     SplitPointHolder(ParallelData& pd, std::vector<std::shared_ptr<SplitPoint>>& spVec, 
-                      std::vector<std::shared_ptr<SplitPoint>>& pending); 
-   
-     /** Destructor. Cancel SplitPoint. */ 
-     ~SplitPointHolder(); 
-   
-     SplitPointHolder(const SplitPointHolder&) = delete; 
-     SplitPointHolder& operator=(const SplitPointHolder&) = delete; 
-   
-     /** Set the SplitPoint object. */ 
-     void setSp(const std::shared_ptr<SplitPoint>& sp); 
-   
-     /** Add a move to the SplitPoint. */ 
-     void addMove(int moveNo, const SplitPointMove& spMove); 
-   
-     /** Add SplitPoint to work queue. If the queue is contended, store SplitPoint 
-      * in pending vector instead. If queue is not contended, also insert all 
-      * objects from the pending vector and clear the pending vector. */ 
-     void addToQueue(); 
-   
-     /** Set which move number the SplitPoint owner is currently searching. 
-      * @return Score from helper search thread. UNKNOWN_SCORE if no helper has 
-      *         searched the move. */ 
-     int setOwnerCurrMove(int moveNo, int alpha); 
-   
-     /** For debugging. */ 
-     U64 getSeqNo() const; 
-   
-     /** Return true if the held SplitPoint is an estimated ALL node. */ 
-     bool isAllNode() const; 
-   
-     /** Return true if some other thread is helping the help SplitPoint. */ 
-     bool hasHelperThread() const; 
-   
- private: 
-     ParallelData& pd; 
-     std::vector<std::shared_ptr<SplitPoint>>& spVec; 
-     std::vector<std::shared_ptr<SplitPoint>>& pending; 
-     std::shared_ptr<SplitPoint> sp; 
-     enum class State { EMPTY, CREATED, QUEUED } state; 
- }; 
-   
- /** Dummy version of SplitPointHolder class. */ 
- struct DummySplitPointHolder { 
-     DummySplitPointHolder(ParallelData& pd, std::vector<std::shared_ptr<SplitPoint>>& spVec, 
-                           std::vector<std::shared_ptr<SplitPoint>>& pending) {} 
-     void setSp(const std::shared_ptr<SplitPoint>& sp) {} 
-     void addMove(int moveNo, const SplitPointMove& spMove) {} 
-     void addToQueue() {} 
-     int setOwnerCurrMove(int moveNo, int alpha) { return SearchConst::UNKNOWN_SCORE; } 
-     bool isAllNode() const { return false; } 
- }; 
-   
- template <bool smp> struct SplitPointTraits { 
- }; 
- template<> struct SplitPointTraits<true> { 
-     using SpHolder = SplitPointHolder; 
- }; 
- template<> struct SplitPointTraits<false> { 
-     using SpHolder = DummySplitPointHolder; 
- }; 
-   
-   
- inline bool 
- WorkerThread::threadRunning() const { 
-     return thread != nullptr; 
- } 
-   
- inline int 
- WorkerThread::getThreadNo() const { 
-     return threadNo; 
- } 
-   
- inline double 
- WorkerThread::getPUseful() const { 
-     return pUseful; 
- } 
-   
- inline 
- WorkQueue::WorkQueue(const FailHighInfo& fhInfo0, const DepthNpsInfo& npsInfo0) 
-     : stopped(false), fhInfo(fhInfo0), npsInfo(npsInfo0) { 
-     resetSplitDepth(); 
- } 
-   
- inline bool 
- WorkQueue::isStopped() const { 
-     return stopped; 
- } 
-   
- inline void 
- WorkQueue::resetSplitDepth() { 
-     minSplitDepth = SearchConst::MIN_SMP_DEPTH; 
-     nContended = 0; 
-     nNonContended = 0; 
- } 
-   
- inline int 
- WorkQueue::getMinSplitDepth() const { 
-     return minSplitDepth; 
- } 
-   
- inline void 
- WorkQueue::insertInQueue(const std::shared_ptr<SplitPoint>& sp) { 
-     bool wasEmpty = queue.empty() || (queue.front()->getPrio() < 0); 
-     queue.insert(sp, sp->getSpPrio(npsInfo)); 
-     sp->setInserted(); 
-     if (wasEmpty) 
-         cv.notify_all(); 
- } 
-   
- inline void 
- WorkQueue::resetStat() { 
-     for (auto& s : wqStat) 
-         s.reset(); 
- } 
-   
- inline TimeSampleStatistics& 
- WorkQueue::getAddWorkStat(int th) { 
-     assert(th < maxStatThreads); 
-     return wqStat[th]; 
- } 
-   
- inline TimeSampleStatistics& 
- WorkQueue::getGetWorkStat(int th) { 
-     assert(th < maxStatThreads); 
-     return wqStat[maxStatThreads+th]; 
- } 
-   
- inline void 
- WorkQueue::printStats(std::ostream& os, int nThreads) { 
-     for (int i = 0; i < nThreads; i++) { 
-         os << "th:" << i << " add: "; 
-         getAddWorkStat(i).printNs(os); 
-         os << " get: "; 
-         getGetWorkStat(i).printNs(os); 
-         os << std::endl; 
-     } 
- } 
-   
-   
- inline int 
- FailHighInfo::getNodeType(int moveNo, bool allNode) const { 
-     if (moveNo == 0) 
-         return allNode ? 0 : 3; 
-     else if (moveNo > 0) 
-         return 1; 
-     else 
-         return 2; 
- } 
-   
- inline 
- DepthNpsInfo::DepthNpsInfo() { 
-     reset(); 
- } 
-   
- inline 
- DepthNpsInfo::NpsData::NpsData() 
-     : nSearches(0), nodes(0), time(0) { 
- } 
-   
- inline void 
- DepthNpsInfo::reset() { 
-     std::lock_guard<std::mutex> L(mutex); 
-     for (int i = 0; i < maxDepth; i++) { 
-         npsData[i].nSearches = 0; 
-         npsData[i].nodes = 0; 
-         npsData[i].time = 0; 
-     } 
-     nps0 = 0; 
-     nSearches = 0; 
-     waitTime = 0; 
- } 
-   
- inline void 
- DepthNpsInfo::setBaseNps(double nps) { 
-     std::lock_guard<std::mutex> L(mutex); 
-     nps0 = nps; 
- } 
-   
- inline void 
- DepthNpsInfo::addData(int depth, U64 nodes, double wTime, double searchTime) { 
-     if (depth >= maxDepth) 
-         depth = maxDepth - 1; 
-     std::lock_guard<std::mutex> L(mutex); 
-     npsData[depth].nSearches++; 
-     npsData[depth].nodes += nodes; 
-     npsData[depth].time += searchTime; 
-     nSearches++; 
-     waitTime += wTime; 
- } 
-   
- inline double 
- DepthNpsInfo::efficiency(int depth) const { 
-     if (depth >= maxDepth) 
-         depth = maxDepth - 1; 
-     std::lock_guard<std::mutex> L(mutex); 
-     return efficiencyInternal(depth); 
- } 
-   
- inline double 
- DepthNpsInfo::efficiencyInternal(int depth) const { 
-     if ((npsData[depth].time > 0) && (nps0 > 0)) { 
-         U64 nodes = npsData[depth].nodes; 
-         double time = npsData[depth].time; 
-         const U32 ns = npsData[depth].nSearches; 
-         if (nSearches > 0) 
-             time += waitTime / nSearches * ns; 
-         double nps = nodes / time; 
-         double eff = nps / nps0; 
-         if (eff > 1.0) 
-             eff = 1.0; 
-         return (eff * ns + 1 * 500) / (ns + 500); 
-     } else 
-         return 1.0; 
- } 
-   
- inline void 
- WorkQueue::Lock::wait(std::condition_variable& cv) { 
-     cv.wait(lock); 
- } 
-   
-   
- inline ParallelData::ParallelData(TranspositionTable& tt0) 
-     : wq(fhInfo, npsInfo), t0Index(0), tt(tt0), 
-       totalHelperNodes(0), helperTbHits(0) { 
- } 
-   
- inline int 
- ParallelData::numHelperThreads() const { 
-     return (int)threads.size(); 
- } 
-   
- inline S64 
- ParallelData::getNumSearchedNodes() const { 
-     return totalHelperNodes; 
- } 
-   
- inline S64 
- ParallelData::getTbHits() const { 
-     return helperTbHits; 
- } 
-   
- inline void 
- ParallelData::addSearchedNodes(S64 nNodes) { 
-     totalHelperNodes += nNodes; 
- } 
-   
- inline void 
- ParallelData::addTbHits(S64 nTbHits) { 
-     helperTbHits += nTbHits; 
- } 
-   
- inline const WorkerThread& 
- ParallelData::getHelperThread(int i) const { 
-     return *threads[i]; 
- } 
-   
-   
- inline bool 
- SplitPoint::isCanceled() const { 
-     return canceled; 
- } 
-   
- inline void 
- SplitPoint::setSeqNo() { 
-     seqNo = nextSeqNo++; 
- } 
-   
- inline std::shared_ptr<SplitPoint> 
- SplitPoint::getParent() const { 
-     return parent; 
- } 
-   
- inline const std::vector<std::weak_ptr<SplitPoint>>& 
- SplitPoint::getChildren() const { 
-     return children; 
- } 
-   
- inline U64 
- SplitPoint::getSeqNo() const { 
-     return seqNo; 
- } 
-   
- inline double 
- SplitPoint::getPSpUseful() const { 
-     return pSpUseful; 
- } 
-   
- inline double 
- SplitPoint::getPNextMoveUseful() const { 
-     return pNextMoveUseful; 
- } 
-   
- inline const History& 
- SplitPoint::getHistory() const { 
-     return ht; 
- } 
-   
- inline const KillerTable& 
- SplitPoint::getKillerTable() const { 
-     return kt; 
- } 
-   
- inline const SplitPointMove& 
- SplitPoint::getSpMove(int moveNo) const { 
-     return spMoves[moveNo]; 
- } 
-   
- inline const SearchTreeInfo& 
- SplitPoint::getSearchTreeInfo() const { 
-     return searchTreeInfo; 
- } 
-   
- inline int 
- SplitPoint::getAlpha() const { 
-     return alpha; 
- } 
-   
- inline int 
- SplitPoint::getBeta() const { 
-     return beta; 
- } 
-   
- inline int 
- SplitPoint::getPly() const { 
-     return ply; 
- } 
-   
- inline int 
- SplitPoint::getDepth() const { 
-     return depth; 
- } 
-   
- inline void 
- SplitPoint::returnMove(int moveNo) { 
-     assert((moveNo >= 0) && (moveNo < (int)spMoves.size())); 
-     SplitPointMove& spm = spMoves[moveNo]; 
-     spm.setSearching(false); 
- } 
-   
- inline int 
- SplitPoint::setOwnerCurrMove(int moveNo, int newAlpha) { 
-     assert((moveNo >= 0) && (moveNo < (int)spMoves.size())); 
-     int score = spMoves[moveNo].getScore(); 
-     spMoves[moveNo].setCanceled(true); 
-     currMoveNo = moveNo; 
-     if (newAlpha > alpha) 
-         alpha = newAlpha; 
-     return score; 
- } 
-   
- inline void 
- SplitPoint::cancel() { 
-     canceled = true; 
-     for (SplitPointMove& spMove : spMoves) 
-         spMove.setCanceled(true); 
- } 
-   
- inline void 
- SplitPoint::addChild(const std::weak_ptr<SplitPoint>& child) { 
-     children.push_back(child); 
- } 
-   
- inline bool 
- SplitPoint::isPvNode() const { 
-     return isPV; 
- } 
-   
- inline int 
- SplitPoint::getSpPrio(const DepthNpsInfo& npsInfo) const { 
-     if (!hasUnStartedMove()) 
-         return -1; 
-     return (int)(getPNextMoveUseful() * npsInfo.efficiency(getDepth()) * 1000); 
- } 
-   
- inline int 
- SplitPoint::owningThread() const { 
-     return threadNo; 
- } 
-   
- inline bool 
- SplitPoint::wasInserted() const { 
-     return inserted; 
- } 
-   
- inline void 
- SplitPoint::setInserted() { 
-     inserted = true; 
- } 
-   
- inline int 
- SplitPoint::getParentMoveNo() const { 
-     return parentMoveNo; 
- } 
-   
- inline int 
- SplitPoint::getCurrMoveNo() const { 
-     return currMoveNo; 
- } 
-   
-   
- inline 
- SplitPointMove::SplitPointMove(const Move& move0, int lmr0, int depth0, 
-                               int captSquare0, bool inCheck0) 
-     : move(move0), lmr(lmr0), depth(depth0), captSquare(captSquare0), 
-       inCheck(inCheck0), canceled(false), searching(false), 
-       score(SearchConst::UNKNOWN_SCORE) { 
- } 
-   
- inline const Move& 
- SplitPointMove::getMove() const { 
-     return move; 
- } 
-   
- inline int 
- SplitPointMove::getLMR() const { 
-     return lmr; 
- } 
-   
- inline int 
- SplitPointMove::getDepth() const { 
-     return depth; 
- } 
-   
- inline int 
- SplitPointMove::getRecaptureSquare() const { 
-     return captSquare; 
- } 
-   
- inline bool 
- SplitPointMove::getInCheck() const { 
-     return inCheck; 
- } 
-   
- inline bool 
- SplitPointMove::isCanceled() const { 
-     return canceled; 
- } 
-   
- inline void 
- SplitPointMove::setCanceled(bool value) { 
-     canceled = value; 
- } 
-   
- inline bool 
- SplitPointMove::isSearching() const { 
-     return searching; 
- } 
-   
- inline void 
- SplitPointMove::setSearching(bool value) { 
-     searching = value; 
- } 
-   
- inline void 
- SplitPointMove::setScore(int s) { 
-     score = s; 
- } 
-   
- inline int 
- SplitPointMove::getScore() const { 
-     return score; 
- } 
-   
-   
- inline 
- SplitPointHolder::SplitPointHolder(ParallelData& pd0, 
-                                    std::vector<std::shared_ptr<SplitPoint>>& spVec0, 
-                                    std::vector<std::shared_ptr<SplitPoint>>& pending0) 
-     : pd(pd0), spVec(spVec0), pending(pending0), 
-       state(State::EMPTY) { 
- } 
-   
- inline 
- SplitPointHolder::~SplitPointHolder() { 
-     if (state == State::QUEUED) { 
- //        log([&](std::ostream& os){os << "cancel seqNo:" << sp->getSeqNo();}); 
-         if (sp->wasInserted()) 
-             pd.wq.cancel(sp); 
-         else { 
-             if (pending.size() > 0) { 
-                 assert(pending[pending.size()-1] == sp); 
-                 pending.pop_back(); 
-             } 
-         } 
-         assert(!spVec.empty()); 
-         spVec.pop_back(); 
-     } 
- } 
-   
- inline void 
- SplitPointHolder::addMove(int moveNo, const SplitPointMove& spMove) { 
-     assert(state == State::CREATED); 
-     sp->addMove(moveNo, spMove); 
- } 
-   
- inline int 
- SplitPointHolder::setOwnerCurrMove(int moveNo, int alpha) { 
- //    if (sp->hasHelperThread()) 
- //        log([&](std::ostream& os){os << "seqNo:" << sp->getSeqNo() << " currMove:" << moveNo 
- //                                     << " a:" << alpha;}); 
-     if (sp->wasInserted()) 
-         return pd.wq.setOwnerCurrMove(sp, moveNo, alpha); 
-     else 
-         return sp->setOwnerCurrMove(moveNo, alpha); 
- } 
-   
- inline U64 
- SplitPointHolder::getSeqNo() const { 
-     return sp->getSeqNo(); 
- } 
-   
- inline bool 
- SplitPointHolder::isAllNode() const { 
-     return sp->isAllNode(); 
- } 
-   
- inline bool 
- SplitPointHolder::hasHelperThread() const { 
-     return sp->hasHelperThread(); 
- } 
-   
- #endif /* PARALLEL_HPP_ */ 
-