Subversion Repositories Games.Chess Giants

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
99 pmbaty 1
/*
2
    Texel - A UCI chess engine.
3
    Copyright (C) 2013-2014  Peter Ă–sterlund, peterosterlund2@gmail.com
4
 
5
    This program is free software: you can redistribute it and/or modify
6
    it under the terms of the GNU General Public License as published by
7
    the Free Software Foundation, either version 3 of the License, or
8
    (at your option) any later version.
9
 
10
    This program is distributed in the hope that it will be useful,
11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
    GNU General Public License for more details.
14
 
15
    You should have received a copy of the GNU General Public License
16
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
*/
18
 
19
/*
20
 * parallel.cpp
21
 *
22
 *  Created on: Jul 9, 2013
23
 *      Author: petero
24
 */
25
 
26
#include "parallel.hpp"
27
#include "numa.hpp"
28
#include "search.hpp"
29
#include "tbprobe.hpp"
30
#include "textio.hpp"
31
#include "util/logger.hpp"
32
 
33
#include <cmath>
34
#include <cassert>
35
 
36
using namespace Logger;
37
 
38
U64 SplitPoint::nextSeqNo = 0;
39
 
40
// ----------------------------------------------------------------------------
41
 
42
WorkerThread::WorkerThread(int threadNo0, ParallelData& pd0,
43
                           TranspositionTable& tt0)
44
    : threadNo(threadNo0), pd(pd0), tt(tt0),
45
      pUseful(0.0) {
46
}
47
 
48
WorkerThread::~WorkerThread() {
49
    assert(!thread);
50
}
51
 
52
void
53
WorkerThread::start() {
54
    assert(!thread);
55
    const int minProbeDepth = TBProbe::tbEnabled() ? UciParams::minProbeDepth->getIntPar() : 100;
56
    thread = std::make_shared<std::thread>([this,minProbeDepth](){ mainLoop(minProbeDepth); });
57
}
58
 
59
void
60
WorkerThread::join() {
61
    if (thread) {
62
        thread->join();
63
        thread.reset();
64
    }
65
}
66
 
67
class ThreadStopHandler : public Search::StopHandler {
68
public:
69
    ThreadStopHandler(WorkerThread& wt, ParallelData& pd,
70
                      const SplitPoint& sp, const SplitPointMove& spm,
71
                      const Search& sc, int initialAlpha,
72
                      S64 totalNodes, int myPrio);
73
 
74
    /** Destructor. Report searched nodes to ParallelData object. */
75
    ~ThreadStopHandler();
76
 
77
    ThreadStopHandler(const ThreadStopHandler&) = delete;
78
    ThreadStopHandler& operator=(const ThreadStopHandler&) = delete;
79
 
80
    bool shouldStop();
81
 
82
private:
83
    /** Report searched nodes since last call to ParallelData object. */
84
    void reportNodes(bool force);
85
 
86
    const WorkerThread& wt;
87
    ParallelData& pd;
88
    const SplitPoint& sp;
89
    const SplitPointMove& spMove;
90
    const Search& sc;
91
    int counter;             // Counts number of calls to shouldStop
92
    int nextProbCheck;       // Next time test for SplitPoint switch should be performed
93
    S64 lastReportedNodes;
94
    S64 lastReportedTbHits;
95
    int initialAlpha;
96
    const S64 totalNodes;
97
    const int myPrio;
98
};
99
 
100
ThreadStopHandler::ThreadStopHandler(WorkerThread& wt0, ParallelData& pd0,
101
                                     const SplitPoint& sp0, const SplitPointMove& spm0,
102
                                     const Search& sc0, int initialAlpha0,
103
                                     S64 totalNodes0, int myPrio0)
104
    : wt(wt0), pd(pd0), sp(sp0), spMove(spm0),
105
      sc(sc0), counter(0), nextProbCheck(1),
106
      lastReportedNodes(0), lastReportedTbHits(0),
107
      initialAlpha(initialAlpha0), totalNodes(totalNodes0), myPrio(myPrio0) {
108
}
109
 
110
ThreadStopHandler::~ThreadStopHandler() {
111
    reportNodes(true);
112
}
113
 
114
bool
115
ThreadStopHandler::shouldStop() {
116
    if (pd.wq.isStopped() || spMove.isCanceled())
117
        return true;
118
    if (sp.getAlpha() != initialAlpha)
119
        return true;
120
 
121
    counter++;
122
    if (counter >= nextProbCheck) {
123
        nextProbCheck = counter + 1 + counter / 4;
124
        int bestPrio = pd.wq.getBestPrio();
125
//        log([&](std::ostream& os){os << "shouldStop, th:" << wt.getThreadNo() << " myP:" << myPrio << " bestP:" << bestPrio;});
126
        if ((bestPrio > myPrio + 20) && (bestPrio >= (myPrio + (1000 - myPrio) * 0.25)) &&
127
            (sp.owningThread() != wt.getThreadNo()))
128
            return true;
129
        reportNodes(false);
130
    }
131
 
132
    return false;
133
}
134
 
135
void
136
ThreadStopHandler::reportNodes(bool force) {
137
    S64 totNodes = sc.getTotalNodesThisThread();
138
    S64 nodes = totNodes - lastReportedNodes;
139
    if (force || (nodes * 1024 > totalNodes)) {
140
        lastReportedNodes = totNodes;
141
        pd.addSearchedNodes(nodes);
142
        S64 totTbHits = sc.getTbHitsThisThread();
143
        S64 tbHits = totTbHits - lastReportedTbHits;
144
        lastReportedTbHits = totTbHits;
145
        pd.addTbHits(tbHits);
146
    }
147
}
148
 
149
void
150
WorkerThread::mainLoop(int minProbeDepth) {
151
//    log([&](std::ostream& os){os << "mainLoop, th:" << threadNo;});
152
    Numa::instance().bindThread(threadNo);
153
    if (!et)
154
        et = Evaluate::getEvalHashTables();
155
    if (!kt)
156
        kt = std::make_shared<KillerTable>();
157
    if (!ht)
158
        ht = std::make_shared<History>();
159
 
160
    TreeLogger logFile;
161
    logFile.open("/home/petero/treelog.dmp", pd, threadNo);
162
 
163
//    UtilizationTimer uTimer;
164
    std::mutex m;
165
    std::unique_lock<std::mutex> lock(m);
166
    Position pos;
167
    std::shared_ptr<SplitPoint> sp;
168
    for (int iter = 0; ; iter++) {
169
        const bool doTiming = (iter & 15) == 0;
170
        int moveNo = -1;
171
//        uTimer.setPUseful(-1);
172
        const double t0 = doTiming ? currentTime() : -1;
173
        int prio;
174
        std::shared_ptr<SplitPoint> newSp = pd.wq.getWork(moveNo, pd, threadNo, prio, pUseful);
175
        if (!newSp)
176
            break;
177
        const double tStart = doTiming ? currentTime() : -1;
178
 
179
        const SplitPointMove& spMove = newSp->getSpMove(moveNo);
180
        const int depth = spMove.getDepth();
181
        if (depth < 0) { // Move skipped by forward pruning or legality check
182
            pd.wq.moveFinished(newSp, moveNo, false, SearchConst::UNKNOWN_SCORE);
183
            continue;
184
        }
185
        if (sp != newSp) {
186
            sp = newSp;
187
            *ht = sp->getHistory();
188
            *kt = sp->getKillerTable();
189
        }
190
        Search::SearchTables st(tt, *kt, *ht, *et);
191
        sp->getPos(pos, spMove.getMove());
192
        std::vector<U64> posHashList;
193
        int posHashListSize;
194
        sp->getPosHashList(pos, posHashList, posHashListSize);
195
        Search sc(pos, posHashList, posHashListSize, st, pd, sp, logFile);
196
        const U64 rootNodeIdx = logFile.logPosition(pos, sp->owningThread(),
197
                                                    sp->getSearchTreeInfo().nodeIdx, moveNo);
198
        sc.setThreadNo(threadNo);
199
        sc.setMinProbeDepth(minProbeDepth);
200
        const int alpha = sp->getAlpha();
201
        const int beta = sp->getBeta();
202
        const S64 nodes0 = pd.getNumSearchedNodes();
203
        auto stopHandler(std::make_shared<ThreadStopHandler>(*this, pd, *sp, spMove,
204
                                                             sc, alpha, nodes0, prio));
205
        sc.setStopHandler(stopHandler);
206
        const int ply = sp->getPly();
207
        const int lmr = spMove.getLMR();
208
        const int captSquare = spMove.getRecaptureSquare();
209
        const bool inCheck = spMove.getInCheck();
210
        sc.setSearchTreeInfo(ply, sp->getSearchTreeInfo(), spMove.getMove(), moveNo, lmr, rootNodeIdx);
211
        try {
212
//            log([&](std::ostream& os){os << "th:" << threadNo << " seqNo:" << sp->getSeqNo() << " ply:" << ply
213
//                                         << " c:" << sp->getCurrMoveNo() << " m:" << moveNo
214
//                                         << " a:" << alpha << " b:" << beta
215
//                                         << " d:" << depth/SearchConst::plyScale
216
//                                         << " p:" << sp->getPMoveUseful(pd.fhInfo, moveNo) << " start";});
217
//            uTimer.setPUseful(pUseful);
218
            const bool smp = pd.numHelperThreads() > 1;
219
            int score = -sc.negaScout(smp, true, -(alpha+1), -alpha, ply+1,
220
                                      depth, captSquare, inCheck);
221
            if (((lmr > 0) && (score > alpha)) ||
222
                    ((score > alpha) && (score < beta))) {
223
                sc.setSearchTreeInfo(ply, sp->getSearchTreeInfo(), spMove.getMove(), moveNo, 0, rootNodeIdx);
224
                score = -sc.negaScout(smp, true, -beta, -alpha, ply+1,
225
                                      depth + lmr, captSquare, inCheck);
226
            }
227
//            uTimer.setPUseful(0);
228
            bool cancelRemaining = score >= beta;
229
//            log([&](std::ostream& os){os << "th:" << threadNo << " seqNo:" << sp->getSeqNo() << " ply:" << ply
230
//                                         << " c:" << sp->getCurrMoveNo() << " m:" << moveNo
231
//                                         << " a:" << alpha << " b:" << beta << " s:" << score
232
//                                         << " d:" << depth/SearchConst::plyScale << " n:" << sc.getTotalNodesThisThread();});
233
            pd.wq.moveFinished(sp, moveNo, cancelRemaining, score);
234
        } catch (const Search::StopSearch&) {
235
//            log([&](std::ostream& os){os << "th:" << threadNo << " seqNo:" << sp->getSeqNo() << " m:" << moveNo
236
//                                         << " aborted n:" << sc.getTotalNodesThisThread();});
237
            if (!spMove.isCanceled() && !pd.wq.isStopped())
238
                pd.wq.returnMove(sp, moveNo);
239
        }
240
        if (doTiming) {
241
            const double tEnd = currentTime();
242
            pd.npsInfo.addData(sp->getDepth(), sc.getTotalNodesThisThread(), tStart - t0, tEnd - tStart);
243
        }
244
        pUseful = 0.0;
245
    }
246
//    double tElapsed, tUseful, tSleep;
247
//    uTimer.getStats(tElapsed, tUseful, tSleep);
248
//    log([&](std::ostream& os){
249
//        os << "~mainLoop, th:" << threadNo << " useful:" << tUseful / tElapsed
250
//           << " sleep:" << tSleep / tElapsed;
251
//    });
252
//    log([&](std::ostream& os){os << "~mainLoop, th:" << threadNo;});
253
}
254
 
255
// ----------------------------------------------------------------------------
256
 
257
void
258
WorkQueue::setStopped(bool stop) {
259
    Lock L(this);
260
    stopped = stop;
261
    if (stopped)
262
        cv.notify_all();
263
}
264
 
265
void
266
WorkQueue::addWork(const std::shared_ptr<SplitPoint>& sp) {
267
    Lock L(this);
268
//    ScopedTimeSample sts { getAddWorkStat(sp->owningThread()) };
269
    addWorkInternal(sp);
270
}
271
 
272
void
273
WorkQueue::tryAddWork(const std::shared_ptr<SplitPoint>& sp,
274
                      std::vector<std::shared_ptr<SplitPoint>>& pending) {
275
    std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
276
    if (!lock.try_lock()) {
277
        pending.push_back(sp);
278
        return;
279
    }
280
    for (const auto& sp : pending)
281
        addWorkInternal(sp);
282
    pending.clear();
283
    addWorkInternal(sp);
284
}
285
 
286
void
287
WorkQueue::addWorkInternal(const std::shared_ptr<SplitPoint>& sp) {
288
    sp->setSeqNo();
289
    std::shared_ptr<SplitPoint> parent = sp->getParent();
290
    if (parent) {
291
        if (parent->isCanceled())
292
            sp->cancel();
293
        else
294
            parent->addChild(sp);
295
    }
296
    if (sp->hasUnFinishedMove()) {
297
        sp->computeProbabilities(fhInfo, npsInfo);
298
        insertInQueue(sp);
299
    }
300
}
301
 
302
std::shared_ptr<SplitPoint>
303
WorkQueue::getWork(int& spMove, ParallelData& pd, int threadNo) {
304
    int prio;
305
    double pUseful;
306
    return getWork(spMove, pd, threadNo, prio, pUseful);
307
}
308
 
309
std::shared_ptr<SplitPoint>
310
WorkQueue::getWork(int& spMove, ParallelData& pd, int threadNo, int& prio, double& pUseful) {
311
    Lock L(this);
312
    while (true) {
313
        while (queue.empty() && !isStopped())
314
            L.wait(cv);
315
//        ScopedTimeSample sts { getGetWorkStat(threadNo) };
316
        if (isStopped())
317
            return nullptr;
318
        std::shared_ptr<SplitPoint> ret = queue.front();
319
        spMove = ret->getNextMove(pd.fhInfo);
320
        if (spMove < 0) {
321
            L.wait(cv);
322
            continue;
323
        }
324
//        log([&](std::ostream& os){printSpTree(os, pd, threadNo, ret, spMove);});
325
        prio = ret->getPrio();
326
        updateProbabilities(ret);
327
        pUseful = ret->getPMoveUseful(pd.fhInfo, spMove);
328
        return ret;
329
    }
330
}
331
 
332
void
333
WorkQueue::returnMove(const std::shared_ptr<SplitPoint>& sp, int moveNo) {
334
    Lock L(this);
335
    sp->returnMove(moveNo);
336
    updateProbabilities(sp);
337
}
338
 
339
int
340
WorkQueue::setOwnerCurrMove(const std::shared_ptr<SplitPoint>& sp, int moveNo, int alpha) {
341
    Lock L(this);
342
    int score = sp->setOwnerCurrMove(moveNo, alpha);
343
    updateProbabilities(sp);
344
    return score;
345
}
346
 
347
void
348
WorkQueue::cancel(const std::shared_ptr<SplitPoint>& sp) {
349
    Lock L(this);
350
    cancelInternal(sp);
351
}
352
 
353
void
354
WorkQueue::moveFinished(const std::shared_ptr<SplitPoint>& sp, int moveNo,
355
                        bool cancelRemaining, int score) {
356
    Lock L(this);
357
    sp->moveFinished(moveNo, cancelRemaining, score);
358
    updateProbabilities(sp);
359
}
360
 
361
double
362
WorkQueue::getBestProbability(std::shared_ptr<SplitPoint>& bestSp) const {
363
    Lock L(this);
364
    if (queue.empty())
365
        return 0.0;
366
    bestSp = queue.front();
367
    return bestSp->getPNextMoveUseful();
368
}
369
 
370
int
371
WorkQueue::getBestPrio() const {
372
    Lock L(this);
373
    if (queue.empty())
374
        return -1;
375
    return queue.front()->getPrio();
376
}
377
 
378
double
379
WorkQueue::getBestProbability() const {
380
    std::shared_ptr<SplitPoint> bestSp;
381
    return getBestProbability(bestSp);
382
}
383
 
384
void
385
WorkQueue::updateProbabilities(const std::shared_ptr<SplitPoint>& sp) {
386
    if (!sp->hasUnFinishedMove())
387
        queue.remove(sp);
388
    sp->computeProbabilities(fhInfo, npsInfo);
389
}
390
 
391
void
392
WorkQueue::cancelInternal(const std::shared_ptr<SplitPoint>& sp) {
393
    sp->cancel();
394
    queue.remove(sp);
395
 
396
    for (const auto& wChild : sp->getChildren()) {
397
        std::shared_ptr<SplitPoint> child = wChild.lock();
398
        if (child)
399
            cancelInternal(child);
400
    }
401
}
402
 
403
static std::string toPercentStr(double p) {
404
    std::stringstream ss;
405
    int pc = (int)(p * 100);
406
    if (pc == 100)
407
        pc = 99;
408
    ss << std::setfill('0') << std::setw(2) << pc;
409
    return ss.str();
410
}
411
 
412
void
413
WorkQueue::printSpTree(std::ostream& os, const ParallelData& pd,
414
                       int threadNo, const std::shared_ptr<SplitPoint> selectedSp,
415
                       int selectedMove) {
416
    const int numThreads = pd.numHelperThreads() + 1;
417
    std::shared_ptr<SplitPoint> rootSp = queue.front();
418
    assert(rootSp);
419
    while (rootSp->getParent())
420
        rootSp = rootSp->getParent();
421
    std::vector<int> parentThreads(numThreads, -1);
422
    std::vector<std::shared_ptr<SplitPoint>> leaves(numThreads, nullptr);
423
    findLeaves(rootSp, parentThreads, leaves);
424
 
425
    os << "th:" << threadNo << " m: " << selectedMove << ':'
426
       << TextIO::moveToUCIString(selectedSp->getSpMove(selectedMove).getMove())
427
       << " p:" << selectedSp->getPMoveUseful(pd.fhInfo, selectedMove)
428
       << std::endl;
429
    for (int i = 0; i < numThreads; i++) {
430
        std::vector<std::shared_ptr<SplitPoint>> thVec;
431
        for (auto sp = leaves[i]; sp; sp = sp->getParent())
432
            thVec.push_back(sp);
433
        std::reverse(thVec.begin(), thVec.end());
434
        os << "th " << i << ' ';
435
        if (parentThreads[i] < 0)
436
            os << '-';
437
        else
438
            os << parentThreads[i];
439
        os << ' ' << toPercentStr(i == 0 ? 1 : pd.getHelperThread(i-1).getPUseful());
440
        if (!thVec.empty()) {
441
            for (const auto& sp : thVec)
442
                if (sp->owningThread() == i) {
443
                    os << ' ' << std::setw(6) << sp->getSeqNo();
444
                    break;
445
                }
446
        }
447
        for (const auto& sp : thVec) {
448
            if (sp->owningThread() == i) {
449
                int pMove = sp->getParentMoveNo();
450
                os << ' ' << std::setw(2) << pMove << (sp == selectedSp ? '*' : ':');
451
                if (pMove < 0)
452
                    os << "null";
453
                else if (sp->getParent())
454
                    os << TextIO::moveToUCIString(sp->getParent()->getSpMove(pMove).getMove());
455
                else
456
                    os << TextIO::moveToUCIString(pd.topMove);
457
                os << ',' << toPercentStr(sp->getPSpUseful())
458
                   << ':' << toPercentStr(sp->getPNextMoveUseful());
459
                os << ',' << std::setw(2) << sp->getCurrMoveNo() << ':' << std::setw(2) << sp->findNextMove(pd.fhInfo);
460
            } else {
461
                os << "                    ";
462
            }
463
        }
464
        os << std::endl;
465
    }
466
}
467
 
468
void
469
WorkQueue::findLeaves(const std::shared_ptr<SplitPoint>& sp,
470
                      std::vector<int>& parentThreads,
471
                      std::vector<std::shared_ptr<SplitPoint>>& leaves) {
472
    bool isLeaf = true;
473
    const std::vector<std::weak_ptr<SplitPoint>>& children = sp->getChildren();
474
    for (const auto& wChild : children) {
475
        std::shared_ptr<SplitPoint> child = wChild.lock();
476
        if (child && !child->isCanceled()) {
477
            if (child->owningThread() == sp->owningThread()) {
478
                isLeaf = false;
479
            } else {
480
                assert(parentThreads[child->owningThread()] == -1);
481
                parentThreads[child->owningThread()] = sp->owningThread();
482
            }
483
            findLeaves(child, parentThreads, leaves);
484
        }
485
    }
486
    if (isLeaf) {
487
        assert(sp->owningThread() >= 0);
488
        assert(sp->owningThread() < (int)leaves.size());
489
        leaves[sp->owningThread()] = sp;
490
    }
491
}
492
 
493
WorkQueue::Lock::Lock(const WorkQueue* wq0)
494
    : wq(*wq0), lock(wq.mutex, std::defer_lock) {
495
    bool contended = false;
496
    if (!lock.try_lock()) {
497
        contended = true;
498
        lock.lock();
499
    }
500
    if (wq.queue.empty())
501
        contended = false;
502
    U64 c = wq.nContended;
503
    U64 n = wq.nNonContended;
504
    if (contended)
505
        c++;
506
    else
507
        n++;
508
    if (n + c > 30000) {
509
        c /= 2;
510
        n /= 2;
511
        if (c * 100 > n * 50) {
512
            wq.minSplitDepth++;
513
//            std::cout << "contended stat: " << wq.minSplitDepth << " " << c << " " << n << std::endl;
514
        } else if ((c * 100 < n * 25) && (wq.minSplitDepth > SearchConst::MIN_SMP_DEPTH)) {
515
            wq.minSplitDepth--;
516
//            std::cout << "contended stat: " << wq.minSplitDepth << " " << c << " " << n << std::endl;
517
        }
518
    }
519
    wq.nContended = c;
520
    wq.nNonContended = n;
521
}
522
 
523
// ----------------------------------------------------------------------------
524
 
525
void
526
ParallelData::addRemoveWorkers(int numWorkers) {
527
    while (numWorkers < (int)threads.size()) {
528
        assert(!threads.back()->threadRunning());
529
        threads.pop_back();
530
    }
531
    for (int i = threads.size(); i < numWorkers; i++)
532
        threads.push_back(std::make_shared<WorkerThread>(i+1, *this, tt));
533
}
534
 
535
void
536
ParallelData::startAll() {
537
    totalHelperNodes = 0;
538
    helperTbHits = 0;
539
    wq.setStopped(false);
540
    for (auto& thread : threads)
541
        thread->start();
542
}
543
 
544
void
545
ParallelData::stopAll() {
546
    wq.setStopped(true);
547
    for (auto& thread : threads)
548
        thread->join();
549
}
550
 
551
// ----------------------------------------------------------------------------
552
 
553
SplitPoint::SplitPoint(int threadNo0,
554
                       const std::shared_ptr<SplitPoint>& parentSp0, int parentMoveNo0,
555
                       const Position& pos0, const std::vector<U64>& posHashList0,
556
                       int posHashListSize0, const SearchTreeInfo& sti0,
557
                       const KillerTable& kt0, const History& ht0,
558
                       int alpha0, int beta0, int ply0, int depth0)
559
    : pos(pos0), posHashList(posHashList0), posHashListSize(posHashListSize0),
560
      searchTreeInfo(sti0), kt(kt0), ht(ht0),
561
      alpha(alpha0), beta(beta0), ply(ply0), depth(depth0),
562
      isPV(beta0 > alpha0 + 1),
563
      pSpUseful(0.0), pNextMoveUseful(0.0),
564
      threadNo(threadNo0), parent(parentSp0), parentMoveNo(parentMoveNo0),
565
      seqNo(0), currMoveNo(0), inserted(false), canceled(false) {
566
}
567
 
568
void
569
SplitPoint::addMove(int moveNo, const SplitPointMove& spMove) {
570
    assert(moveNo >= (int)spMoves.size());
571
    while ((int)spMoves.size() < moveNo)
572
        spMoves.push_back(SplitPointMove(Move(), 0, -1, -1, false));
573
    spMoves.push_back(spMove);
574
}
575
 
576
void
577
SplitPoint::computeProbabilities(const FailHighInfo& fhInfo, const DepthNpsInfo& npsInfo) {
578
    if (parent) {
579
        double pMoveUseful = 1.0;
580
        if (parentMoveNo >= 0)
581
            pMoveUseful = parent->getMoveNeededProbability(fhInfo, parentMoveNo);
582
        pSpUseful = parent->pSpUseful * pMoveUseful;
583
    } else {
584
        pSpUseful = 1.0;
585
    }
586
    double pNextUseful = getMoveNeededProbability(fhInfo, findNextMove(fhInfo));
587
    pNextMoveUseful = pSpUseful * pNextUseful;
588
    newPrio(getSpPrio(npsInfo));
589
 
590
    bool deleted = false;
591
    for (const auto& wChild : children) {
592
        std::shared_ptr<SplitPoint> child = wChild.lock();
593
        if (child)
594
            child->computeProbabilities(fhInfo, npsInfo);
595
        else
596
            deleted = true;
597
    }
598
    if (deleted)
599
        cleanUpChildren();
600
}
601
 
602
double
603
SplitPoint::getPMoveUseful(const FailHighInfo& fhInfo, int moveNo) const {
604
    return pSpUseful * getMoveNeededProbability(fhInfo, moveNo);
605
}
606
 
607
void
608
SplitPoint::getPos(Position& pos, const Move& move) const {
609
    pos = this->pos;
610
    UndoInfo ui;
611
    pos.makeMove(move, ui);
612
}
613
 
614
void
615
SplitPoint::getPosHashList(const Position& pos, std::vector<U64>& posHashList,
616
                           int& posHashListSize) const {
617
    posHashList = this->posHashList;
618
    posHashListSize = this->posHashListSize;
619
    posHashList[posHashListSize++] = pos.zobristHash();
620
}
621
 
622
int
623
SplitPoint::getNextMove(const FailHighInfo& fhInfo) {
624
    int m = findNextMove(fhInfo);
625
    if (m < 0)
626
        return m;
627
    spMoves[m].setSearching(true);
628
    return m;
629
}
630
 
631
void
632
SplitPoint::moveFinished(int moveNo, bool cancelRemaining, int score) {
633
    assert((moveNo >= 0) && (moveNo < (int)spMoves.size()));
634
    spMoves[moveNo].setScore(score);
635
    spMoves[moveNo].setSearching(false);
636
    spMoves[moveNo].setCanceled(true);
637
    if (cancelRemaining)
638
        for (int i = moveNo+1; i < (int)spMoves.size(); i++)
639
            spMoves[i].setCanceled(true);
640
}
641
 
642
bool
643
SplitPoint::hasUnStartedMove() const {
644
    if (canceled)
645
        return false;
646
    for (int i = currMoveNo + 1; i < (int)spMoves.size(); i++)
647
        if (!spMoves[i].isCanceled() && !spMoves[i].isSearching())
648
            return true;
649
    return false;
650
}
651
 
652
bool
653
SplitPoint::hasUnFinishedMove() const {
654
    if (canceled)
655
        return false;
656
    for (int i = currMoveNo + 1; i < (int)spMoves.size(); i++)
657
        if (!spMoves[i].isCanceled())
658
            return true;
659
    return false;
660
}
661
 
662
int
663
SplitPoint::findNextMove(const FailHighInfo& fhInfo) const {
664
    int i0 = -1;
665
    const double pGood = 0.98;
666
    for (int i = currMoveNo+1; i < (int)spMoves.size(); i++)
667
        if (!spMoves[i].isCanceled() && !spMoves[i].isSearching()) {
668
            if ((getPNextMoveUseful() > pGood) && (i0 == -1))
669
                i0 = i;
670
            else {
671
                if ((i0 != -1) && (getPMoveUseful(fhInfo, i) <= pGood))
672
                    return i0;
673
                return i;
674
            }
675
        }
676
    return i0;
677
}
678
 
679
double
680
SplitPoint::getMoveNeededProbability(const FailHighInfo& fhInfo, int moveNo) const {
681
    if (isPvNode())
682
        return fhInfo.getMoveNeededProbabilityPv(currMoveNo, moveNo);
683
    else
684
        return fhInfo.getMoveNeededProbability(parentMoveNo,
685
                                               currMoveNo,
686
                                               moveNo, isAllNode());
687
}
688
 
689
void
690
SplitPoint::cleanUpChildren() {
691
    std::vector<std::weak_ptr<SplitPoint>> toKeep;
692
    for (const auto& wChild : children)
693
        if (wChild.lock())
694
            toKeep.push_back(wChild);
695
    children = toKeep;
696
}
697
 
698
bool
699
SplitPoint::hasHelperThread() const {
700
    for (const auto& wChild : children) {
701
        std::shared_ptr<SplitPoint> child = wChild.lock();
702
        if (child && child->owningThread() != owningThread())
703
            return true;
704
    }
705
    return false;
706
}
707
 
708
bool
709
SplitPoint::isAncestorTo(const SplitPoint& sp) const {
710
    const SplitPoint* tmp = &sp;
711
    while (tmp) {
712
        if (tmp == this)
713
            return true;
714
        tmp = &*(tmp->parent);
715
    }
716
    return false;
717
}
718
 
719
bool
720
SplitPoint::isAllNode() const {
721
    int nFirst = 0;
722
    const SplitPoint* tmp = this;
723
    while (tmp) {
724
        if (tmp->parentMoveNo == 0)
725
            nFirst++;
726
        else
727
            break;
728
        tmp = &*(tmp->parent);
729
    }
730
    return (nFirst % 2) != 0;
731
}
732
 
733
void
734
SplitPoint::print(std::ostream& os, int level, const FailHighInfo& fhInfo) const {
735
    std::string pad(level*2, ' ');
736
    os << pad << "seq:" << seqNo << " pos:" << TextIO::toFEN(pos) << std::endl;
737
    os << pad << "parent:" << parentMoveNo << " hashListSize:" << posHashListSize <<
738
        " a:" << alpha << " b:" << beta << " ply:" << ply << " canceled:" << canceled << std::endl;
739
    os << pad << "p1:" << pSpUseful << " p2:" << pNextMoveUseful << " curr:" << currMoveNo << std::endl;
740
    os << pad << "moves:";
741
    for (int mi = 0; mi < (int)spMoves.size(); mi++) {
742
        const auto& spm = spMoves[mi];
743
        os << ' ' << TextIO::moveToUCIString(spm.getMove());
744
        if (spm.isCanceled())
745
            os << ",c";
746
        if (spm.isSearching())
747
            os << ",s";
748
        os << "," << getMoveNeededProbability(fhInfo, mi);
749
    }
750
    os << std::endl;
751
    for (const auto& wChild : children) {
752
        std::shared_ptr<SplitPoint> child = wChild.lock();
753
        if (child)
754
            child->print(os, level+1, fhInfo);
755
    }
756
}
757
 
758
// ----------------------------------------------------------------------------
759
 
760
void
761
SplitPointHolder::setSp(const std::shared_ptr<SplitPoint>& sp0) {
762
    assert(state == State::EMPTY);
763
    assert(sp0);
764
    sp = sp0;
765
    if (sp->getBeta() > sp->getAlpha() + 1)
766
        pd.fhInfo.addPvData(-1, false);
767
    state = State::CREATED;
768
}
769
 
770
void
771
SplitPointHolder::addToQueue() {
772
    assert(state == State::CREATED);
773
    pd.wq.tryAddWork(sp, pending);
774
    spVec.push_back(sp);
775
//    log([&](std::ostream& os){os << "add seqNo:" << sp->getSeqNo() << " ply:" << sp->getPly()
776
//                                 << " pNext:" << sp->getPNextMoveUseful()
777
//                                 << " pMove:" << sp->getParentMoveNo() << " vec:" << spVec.size();});
778
    state = State::QUEUED;
779
}
780
 
781
// ----------------------------------------------------------------------------
782
 
783
FailHighInfo::FailHighInfo()
784
    : totCount(0) {
785
    for (int i = 0; i < NUM_NODE_TYPES; i++)
786
        failLoCount[i] = 0;
787
    for (int j = 0; j < NUM_STAT_MOVES; j++)
788
        newAlpha[j] = 0;
789
    totPvCount = 0;
790
}
791
 
792
double
793
FailHighInfo::getMoveNeededProbability(int parentMoveNo,
794
                                       int currMoveNo, int moveNo, bool allNode) const {
795
    const int pIdx = getNodeType(parentMoveNo, allNode);
796
    moveNo = std::min(moveNo, NUM_STAT_MOVES-1);
797
    if (moveNo < 0)
798
        return 0.0;
799
 
800
    int nNeeded = failLoCount[pIdx] + failHiCount[pIdx].sum(moveNo, NUM_STAT_MOVES);
801
    int nTotal = nNeeded + failHiCount[pIdx].sum(currMoveNo, moveNo);
802
 
803
    return (nTotal > 0) ? nNeeded / (double)nTotal : 0.5;
804
}
805
 
806
double
807
FailHighInfo::getMoveNeededProbabilityPv(int currMoveNo, int moveNo) const {
808
    moveNo = std::min(moveNo, NUM_STAT_MOVES-1);
809
    if (moveNo < 0)
810
        return 0.0;
811
    if (totPvCount <= 0)
812
        return 0.5;
813
 
814
    double prob = 1.0;
815
    double inv = 1.0 / totPvCount;
816
    for (int i = currMoveNo; i < moveNo; i++)
817
        prob *= std::max(0.0, 1.0 - newAlpha[i] * inv);
818
    return prob;
819
}
820
 
821
void
822
FailHighInfo::addData(int parentMoveNo, int nSearched, bool failHigh, bool allNode) {
823
    if (nSearched < 0)
824
        return;
825
    const int pIdx = getNodeType(parentMoveNo, allNode);
826
    if (failHigh) {
827
        nSearched = std::min(nSearched, NUM_STAT_MOVES-1);
828
        failHiCount[pIdx].add(nSearched, 1);
829
    } else {
830
        failLoCount[pIdx]++;
831
    }
832
    totCount++;
833
    if (totCount >= 1000000) {
834
        std::lock_guard<std::mutex> L(mutex);
835
        if (totCount >= 1000000)
836
            reScaleInternal(2);
837
    }
838
}
839
 
840
void
841
FailHighInfo::addPvData(int nSearched, bool alphaChanged) {
842
    if (nSearched >= 0) {
843
        if (alphaChanged && nSearched < NUM_STAT_MOVES)
844
            newAlpha[nSearched]++;
845
    } else {
846
        totPvCount++;
847
        if (totPvCount >= 10000) {
848
            std::lock_guard<std::mutex> L(mutex);
849
            if (totPvCount >= 10000)
850
                reScalePv(2);
851
        }
852
    }
853
}
854
 
855
void
856
FailHighInfo::reScale() {
857
    reScaleInternal(4);
858
    reScalePv(4);
859
}
860
 
861
void
862
FailHighInfo::reScaleInternal(int factor) {
863
    for (int i = 0; i < NUM_NODE_TYPES; i++) {
864
        for (int j = 0; j < NUM_STAT_MOVES; j++) {
865
            int val = failHiCount[i].get(j);
866
            failHiCount[i].add(j, val / factor - val);
867
        }
868
        failLoCount[i] = failLoCount[i] / factor;
869
    }
870
    totCount = totCount / factor;
871
}
872
 
873
void
874
FailHighInfo::reScalePv(int factor) {
875
    for (int j = 0; j < NUM_STAT_MOVES; j++)
876
        newAlpha[j] = newAlpha[j] / factor;
877
    totPvCount = totPvCount / factor;
878
}
879
 
880
void
881
FailHighInfo::print(std::ostream& os) const {
882
    for (int i = 0; i < NUM_NODE_TYPES; i++) {
883
        os << "fhInfo: " << i << ' ' << std::setw(6) << failLoCount[i];
884
        for (int j = 0; j < NUM_STAT_MOVES; j++)
885
            os << ' ' << std::setw(6) << failHiCount[i].get(j);
886
        os << std::endl;
887
    }
888
    os << "fhInfo: " << NUM_NODE_TYPES << ' ' << std::setw(6) << totPvCount;
889
    for (int j = 0; j < NUM_STAT_MOVES; j++)
890
        os << ' ' << std::setw(6) << newAlpha[j];
891
    os << std::endl;
892
}
893
 
894
// ----------------------------------------------------------------------------
895
 
896
void
897
DepthNpsInfo::print(std::ostream& os, int iterDepth) {
898
    std::lock_guard<std::mutex> L(mutex);
899
    os << "npsInfo: depth:" << iterDepth << " nps0:" << nps0
900
       << " wait:" << waitTime / nSearches << std::endl;
901
    for (int i = SearchConst::MIN_SMP_DEPTH; i < maxDepth; i++) {
902
        os << "npsInfo: d:" << i
903
           << " n:" << npsData[i].nSearches
904
           << " nodes:" << npsData[i].nodes
905
           << " time:" << npsData[i].time
906
           << " nps:" << npsData[i].nodes / npsData[i].time
907
           << " ts:" << npsData[i].nodes / (double)npsData[i].nSearches
908
           << " eff:" << efficiencyInternal(i) << std::endl;
909
    }
910
}