From b73fed93d555c571e42173166a7087c14bb54304 Mon Sep 17 00:00:00 2001 From: syzygy1 <3028851+syzygy1@users.noreply.github.com> Date: Sun, 28 Jun 2020 20:49:38 +0200 Subject: [PATCH] Addition of "Introduce coordination between searching threads". This patch had been skipped earlier: https://github.com/official-stockfish/Stockfish/commit/217840a6a5a40b516cab59a450a9f36352997240 The implementation in Cfish is a bit different but should be functionally more or less equivalent (SMP noise will make it impossible to see a difference in search efficiency, unless a bug crept in). --- src/ntsearch.c | 34 ++++++++++++++++++++++++++++++++-- src/search.c | 6 +++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/ntsearch.c b/src/ntsearch.c index 8a706065..ea2c2f12 100644 --- a/src/ntsearch.c +++ b/src/ntsearch.c @@ -387,6 +387,26 @@ Value search_NonPV(Pos *pos, Stack *ss, Value alpha, Depth depth, int cutNode) singularLMR = moveCountPruning = false; ttCapture = ttMove && is_capture_or_promotion(pos, ttMove); + // Check for a breadcrumb and leave one if none found + _Atomic uint64_t *crumb = NULL; + bool marked = false; + if (ss->ply < 8) { + crumb = &breadcrumbs[posKey & 1023]; + // The next line assumes there are at most 65535 search threads + uint64_t v = (posKey & ~0xffffULL) | (pos->threadIdx + 1), expected = 0ULL; + // If no crumb is in place yet, leave ours + if (!atomic_compare_exchange_strong_explicit(crumb, &expected, v, + memory_order_relaxed, memory_order_relaxed)) + { + // Some crumb was in place already. Its value is now in expected. + crumb = NULL; + // Was the crumb is for the same position and was left by another thread? + v ^= expected; + if (v != 0 && (v & ~0xffffULL) == 0) + marked = true; + } + } + // Step 12. Loop through moves // Loop through all pseudo-legal moves until no moves remain or a beta // cutoff occurs @@ -507,8 +527,10 @@ Value search_NonPV(Pos *pos, Stack *ss, Value alpha, Depth depth, int cutNode) // assume that this expected cut-node is not singular, i.e. multiple // moves fail high. We therefore prune the whole subtree by returning // a soft bound. - else if (singularBeta >= beta) + else if (singularBeta >= beta) { + if (crumb) store_rlx(*crumb, 0); return singularBeta; + } // The call to search_NonPV with the same value of ss messed up our // move picker data. So we fix it. @@ -575,6 +597,10 @@ Value search_NonPV(Pos *pos, Stack *ss, Value alpha, Depth depth, int cutNode) if (pos->ttHitAverage > 500 * ttHitAverageResolution * ttHitAverageWindow / 1024) r--; + // Reduction if other threads are searching this position. + if (marked) + r++; + // Decrease reduction if position is or has been on the PV if (ttPv) r -= 2; @@ -680,8 +706,10 @@ Value search_NonPV(Pos *pos, Stack *ss, Value alpha, Depth depth, int cutNode) // Finished searching the move. If a stop occurred, the return value of // the search cannot be trusted, and we return immediately without // updating best move, PV and TT. - if (load_rlx(Signals.stop)) + if (load_rlx(Signals.stop)) { + if (crumb) store_rlx(*crumb, 0); return 0; + } if (rootNode) { RootMove *rm = NULL; @@ -739,6 +767,8 @@ Value search_NonPV(Pos *pos, Stack *ss, Value alpha, Depth depth, int cutNode) capturesSearched[captureCount++] = move; } + if (crumb) store_rlx(*crumb, 0); + // The following condition would detect a stop only after move loop has // been completed. But in this case bestValue is valid because we have // fully searched our subtree, and we can anyhow save the result in TT. diff --git a/src/search.c b/src/search.c index 81c1931d..5995cc15 100644 --- a/src/search.c +++ b/src/search.c @@ -102,7 +102,8 @@ struct Skill { // Move best = 0; }; -//static CounterMoveHistoryStat CounterMoveHistory; +// Breadcrumbs are used to mark nodes as being search by a given thread +static _Atomic uint64_t breadcrumbs[1024]; static Value search_PV(Pos *pos, Stack *ss, Value alpha, Value beta, Depth depth); static Value search_NonPV(Pos *pos, Stack *ss, Value alpha, Depth depth, int cutNode); @@ -980,6 +981,9 @@ void start_thinking(Pos *root) Signals.stopOnPonderhit = Signals.stop = 0; Threads.increaseDepth = true; + for (int i = 0; i < 1024; i++) + store_rlx(breadcrumbs[i], 0); + // Generate all legal moves. ExtMove list[MAX_MOVES]; ExtMove *end = generate_legal(root, list);