Use non-locking operations for nodes count and tbHits

fetch_add and friends must produce a locking instruction because they guarantee
consistency in the presence of multiple writers. Because only one thread ever
writes to nodes and tbHits, we may use relaxed load and store to emit regular
loads and stores (on both x86-64 and arm64), while avoiding undefined behavior
and miscounting nodes.

Credit must go to Arseniy Surkov of Reckless (codedeliveryservice) for
uncovering this performance gotcha.

failed yellow as a gainer on fishtest:
LLR: -2.95 (-2.94,2.94) <0.00,2.00>
Total: 219616 W: 57165 L: 57105 D: 105346
Ptnml(0-2): 732, 24277, 59720, 24357, 722
https://tests.stockfishchess.org/tests/view/69086abfea4b268f1fac2809

potential small speedup on large core system:
```
==== master ====
1 Nodes/second : 294269736
2 Nodes/second : 295217629
Average (over 2):  294743682
==== patch ====
1 Nodes/second : 299003603
2 Nodes/second : 298111320
Average (over 2):  298557461
```

closes https://github.com/official-stockfish/Stockfish/pull/6392

No functional change
This commit is contained in:
Timothy Herchen
2025-11-17 13:24:32 +01:00
committed by Joost VandeVondele
parent 1d504b927f
commit b083049fe0
+4 -2
View File
@@ -529,7 +529,8 @@ void Search::Worker::do_move(Position& pos, const Move move, StateInfo& st, Stac
void Search::Worker::do_move(
Position& pos, const Move move, StateInfo& st, const bool givesCheck, Stack* const ss) {
bool capture = pos.capture_stage(move);
nodes.fetch_add(1, std::memory_order_relaxed);
// Preferable over fetch_add to avoid locking instructions
nodes.store(nodes.load(std::memory_order_relaxed) + 1, std::memory_order_relaxed);
auto [dirtyPiece, dirtyThreats] = accumulatorStack.push();
pos.do_move(move, st, givesCheck, dirtyPiece, dirtyThreats, &tt);
@@ -749,7 +750,8 @@ Value Search::Worker::search(
if (err != TB::ProbeState::FAIL)
{
tbHits.fetch_add(1, std::memory_order_relaxed);
// Preferable over fetch_add to avoid locking instructions
tbHits.store(tbHits.load(std::memory_order_relaxed) + 1, std::memory_order_relaxed);
int drawScore = tbConfig.useRule50 ? 1 : 0;