prefetch the TT entry as soon as the move is known

Worker::do_move computes the successor hash key via the new
Position::key_after(m) and prefetches the TT entry one full do_move
earlier than the existing prefetch in Position::do_move. key_after does
not model castling, en passant or promotion keys exactly; for rare
moves the prefetch lands on an unused line.

`key_after` has been around since 2014 (https://github.com/official-stockfish/Stockfish/commit/82d065b0) and was removed in (https://github.com/official-stockfish/Stockfish/pull/5770). Adding back `prefetch_key` helps in common, normal moves at the cost of extra compute.

Speedup (PGO vs PGO, interleaved paired bench, n=48 pairs, Apple M2
Pro / apple-silicon): +0.69% [0.47, 0.91]

Passed STC:
https://tests.stockfishchess.org/tests/view/6a291f8d7c758d82accea17f
LLR: 4.24 (-2.94,2.94) <0.00,2.00>
Total: 473504 W: 121250 L: 120228 D: 232026
Ptnml(0-2): 1112, 51137, 131251, 52121, 1131

No functional change

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

No functional change
This commit is contained in:
Dalton Hanaway
2026-06-25 13:03:44 +02:00
committed by Joost VandeVondele
parent 31e89adf70
commit 6f97594eda
4 changed files with 25 additions and 2 deletions
+1
View File
@@ -62,6 +62,7 @@ Clemens L. (rn5f107s2)
Cody Ho (aesrentai) Cody Ho (aesrentai)
CSTENTOR CSTENTOR
Dale Weiler (graphitemaster) Dale Weiler (graphitemaster)
Dalton Hanaway (dhanaway)
Daniel Axtens (daxtens) Daniel Axtens (daxtens)
Daniel Dugovic (ddugovic) Daniel Dugovic (ddugovic)
Daniel Monroe (daniel-monroe) Daniel Monroe (daniel-monroe)
+15
View File
@@ -1315,6 +1315,21 @@ void Position::update_piece_threats(Piece pc,
#endif #endif
} }
Key Position::prefetch_key(Move m) const {
Square from = m.from_sq();
Square to = m.to_sq();
Piece pc = piece_on(from);
Piece captured = piece_on(to);
Key k = st->key ^ Zobrist::side;
k ^= Zobrist::psq[captured][to] ^ Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from];
if (captured || type_of(pc) == PAWN)
return k;
return adjust_key50<true>(k);
}
// Helper used to do/undo a castling move. This is a bit // Helper used to do/undo a castling move. This is a bit
// tricky in Chess960 where from/to squares can overlap. // tricky in Chess960 where from/to squares can overlap.
template<bool Do> template<bool Do>
+5 -2
View File
@@ -160,6 +160,7 @@ class Position {
// Accessing hash keys // Accessing hash keys
Key key() const; Key key() const;
Key prefetch_key(Move m) const;
Key material_key() const; Key material_key() const;
Key pawn_key() const; Key pawn_key() const;
Key minor_piece_key() const; Key minor_piece_key() const;
@@ -211,7 +212,8 @@ class Position {
Square& rto, Square& rto,
DirtyThreats* const dts = nullptr, DirtyThreats* const dts = nullptr,
DirtyPiece* const dp = nullptr); DirtyPiece* const dp = nullptr);
Key adjust_key50(Key k) const; template<bool AfterMove = false>
Key adjust_key50(Key k) const;
// Data members // Data members
std::array<Piece, SQUARE_NB> board; std::array<Piece, SQUARE_NB> board;
@@ -317,8 +319,9 @@ inline Bitboard Position::check_squares(PieceType pt) const { return st->checkSq
inline Key Position::key() const { return adjust_key50(st->key); } inline Key Position::key() const { return adjust_key50(st->key); }
template<bool AfterMove>
inline Key Position::adjust_key50(Key k) const { inline Key Position::adjust_key50(Key k) const {
return st->rule50 < 14 ? k : k ^ make_key((st->rule50 - 14) / 8); return st->rule50 < 14 - AfterMove ? k : k ^ make_key((st->rule50 - (14 - AfterMove)) / 8);
} }
inline Key Position::pawn_key() const { return st->pawnKey; } inline Key Position::pawn_key() const { return st->pawnKey; }
+4
View File
@@ -634,6 +634,10 @@ void Search::Worker::do_move(Position& pos, const Move move, StateInfo& st, Stac
void Search::Worker::do_move( void Search::Worker::do_move(
Position& pos, const Move move, StateInfo& st, const bool givesCheck, Stack* const ss) { Position& pos, const Move move, StateInfo& st, const bool givesCheck, Stack* const ss) {
// prefetch_key does not model castling, en passant or promotion keys
// exactly; for rare moves the prefetch lands on an unused line.
prefetch(tt.first_entry(pos.prefetch_key(move)));
bool capture = pos.capture_stage(move); bool capture = pos.capture_stage(move);
++nodes; ++nodes;