Shared pawn history

[Passed STC SMP](https://tests.stockfishchess.org/tests/view/694e506c572093c1986d7276):
```
LLR: 2.97 (-2.94,2.94) <0.00,2.00>
Total: 14992 W: 3924 L: 3653 D: 7415
Ptnml(0-2): 20, 1547, 4090, 1820, 19
```

[Passed LTC SMP](https://tests.stockfishchess.org/tests/live_elo/694ead61572093c1986d7365):
```
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 41146 W: 10654 L: 10342 D: 20150
Ptnml(0-2): 17, 3999, 12225, 4319, 13
```

[Passed a sanity check STC SMP post-refactoring](https://tests.stockfishchess.org/tests/view/69503997572093c1986d763a):
```
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 46728 W: 12178 L: 11863 D: 22687
Ptnml(0-2): 82, 5093, 12685, 5436, 68
```

(The large gain of the first STC was probably a fluke, and this result
is more reasonable!)

After shared correction history, Viz suggested we try sharing other
histories, especially `pawnHistory`. As far as we're aware, sharing
history besides correction history (like Caissa does) is novel. The
implementation follows the same pattern as shared correction history –
the size of the history table is scaled with
`next_power_of_two(threadsInNumaNode)` and the entry is prefetched in
`do_move`.

A bit of refactoring was done to accommodate this new history. Note that
we prefetch `&history->pawn_entry(*this)[pc][to]` rather than
`&history->pawn_entry(*this)` because unlike the other entries, each
entry contains multiple cache lines.

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

Bench: 2503391

Co-authored-by: Michael Chaly <Vizvezdenec@gmail.com>
This commit is contained in:
anematode
2025-12-28 14:59:09 +01:00
committed by Disservin
co-authored by Michael Chaly
parent 1780c1fd6e
commit 969285fa5d
6 changed files with 49 additions and 34 deletions
+8 -13
View File
@@ -580,14 +580,10 @@ void Search::Worker::undo_null_move(Position& pos) { pos.undo_null_move(); }
void Search::Worker::clear() {
mainHistory.fill(68);
captureHistory.fill(-689);
pawnHistory.fill(-1238);
// Each thread is responsible for clearing their part of shared history
size_t len = sharedHistory.get_size() / numaTotal;
size_t start = numaThreadIdx * len;
size_t end = std::min(start + len, sharedHistory.get_size());
sharedHistory.correctionHistory.clear_range(start, end);
sharedHistory.correctionHistory.clear_range(0, numaThreadIdx);
sharedHistory.pawnHistory.clear_range(-1238, numaThreadIdx);
ttMoveHistory = 0;
@@ -861,7 +857,7 @@ Value Search::Worker::search(
mainHistory[~us][((ss - 1)->currentMove).raw()] << evalDiff * 9;
if (!ttHit && type_of(pos.piece_on(prevSq)) != PAWN
&& ((ss - 1)->currentMove).type_of() != PROMOTION)
pawnHistory[pawn_history_index(pos)][pos.piece_on(prevSq)][prevSq] << evalDiff * 13;
sharedHistory.pawn_entry(pos)[pos.piece_on(prevSq)][prevSq] << evalDiff * 13;
}
@@ -992,7 +988,7 @@ moves_loop: // When in check, search starts here
MovePicker mp(pos, ttData.move, depth, &mainHistory, &lowPlyHistory, &captureHistory, contHist,
&pawnHistory, ss->ply);
&sharedHistory, ss->ply);
value = bestValue;
@@ -1081,7 +1077,7 @@ moves_loop: // When in check, search starts here
{
int history = (*contHist[0])[movedPiece][move.to_sq()]
+ (*contHist[1])[movedPiece][move.to_sq()]
+ pawnHistory[pawn_history_index(pos)][movedPiece][move.to_sq()];
+ sharedHistory.pawn_entry(pos)[movedPiece][move.to_sq()];
// Continuation history based pruning
if (history < -4083 * depth)
@@ -1439,7 +1435,7 @@ moves_loop: // When in check, search starts here
mainHistory[~us][((ss - 1)->currentMove).raw()] << scaledBonus * 243 / 32768;
if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION)
pawnHistory[pawn_history_index(pos)][pos.piece_on(prevSq)][prevSq]
sharedHistory.pawn_entry(pos)[pos.piece_on(prevSq)][prevSq]
<< scaledBonus * 1160 / 32768;
}
@@ -1611,7 +1607,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
// the moves. We presently use two stages of move generator in quiescence search:
// captures, or evasions only when in check.
MovePicker mp(pos, ttData.move, DEPTH_QS, &mainHistory, &lowPlyHistory, &captureHistory,
contHist, &pawnHistory, ss->ply);
contHist, &sharedHistory, ss->ply);
// Step 5. Loop through all pseudo-legal moves until no moves remain or a beta
// cutoff occurs.
@@ -1900,8 +1896,7 @@ void update_quiet_histories(
update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus * 896 / 1024);
int pIndex = pawn_history_index(pos);
workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()]
workerThread.sharedHistory.pawn_entry(pos)[pos.moved_piece(move)][move.to_sq()]
<< bonus * (bonus > 0 ? 905 : 505) / 1024;
}