From aaafaaecf2aa15806b1c1bd10f6a74379ba094b6 Mon Sep 17 00:00:00 2001 From: Carlos Esparza Date: Sun, 12 Jan 2025 23:22:01 +0100 Subject: [PATCH] prefetch in do_move() this allows removing Position::key_after() STC LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 24960 W: 6556 L: 6336 D: 12068 Ptnml(0-2): 59, 2554, 7056, 2730, 81 LTC LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 115080 W: 29319 L: 29204 D: 56557 Ptnml(0-2): 51, 10736, 35864, 10825, 64 STC with 2MB hash LLR: 3.04 (-2.94,2.94) <-1.75,0.25> Total: 182176 W: 46998 L: 46932 D: 88246 Ptnml(0-2): 526, 19711, 50544, 19785, 522 LTC with 8MB hash LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 441180 W: 111557 L: 111746 D: 217877 Ptnml(0-2): 229, 39698, 140929, 39501, 233 closes https://github.com/official-stockfish/Stockfish/pull/5770 bench: 1379150 --- src/position.cpp | 32 +++++++++++--------------------- src/position.h | 9 +++++---- src/search.cpp | 41 ++++++++++++++++++----------------------- 3 files changed, 34 insertions(+), 48 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 9d883c92..49f520e0 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -689,7 +689,12 @@ bool Position::gives_check(Move m) const { // Makes a move, and saves all information necessary // to a StateInfo object. The move is assumed to be legal. Pseudo-legal // moves should be filtered out before this function is called. -void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { +// If a pointer to the TT table is passed, the entry for the new position +// will be prefetched +void Position::do_move(Move m, + StateInfo& newSt, + bool givesCheck, + const TranspositionTable* tt = nullptr) { assert(m.is_ok()); assert(&newSt != st); @@ -887,11 +892,13 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->minorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; } - // Set capture piece - st->capturedPiece = captured; - // Update the key with the final value st->key = k; + if (tt) + prefetch(tt->first_entry(key())); + + // Set capture piece + st->capturedPiece = captured; // Calculate checkers bitboard (if move gives check) st->checkersBB = givesCheck ? attackers_to(square(them)) & pieces(us) : 0; @@ -1069,23 +1076,6 @@ void Position::undo_null_move() { } -// Computes the new hash key after the given move. Needed -// for speculative prefetch. It doesn't recognize special moves like castling, -// en passant and promotions. -Key Position::key_after(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]; - - return (captured || type_of(pc) == PAWN) ? k : adjust_key50(k); -} - - // Tests if the SEE (Static Exchange Evaluation) // value of move is greater or equal to the given threshold. We'll use an // algorithm similar to alpha-beta pruning with a null window. diff --git a/src/position.h b/src/position.h index 7fdaf84d..0d49a60a 100644 --- a/src/position.h +++ b/src/position.h @@ -141,8 +141,8 @@ class Position { Piece captured_piece() const; // Doing and undoing moves - void do_move(Move m, StateInfo& newSt); - void do_move(Move m, StateInfo& newSt, bool givesCheck); + void do_move(Move m, StateInfo& newSt, const TranspositionTable* tt); + void do_move(Move m, StateInfo& newSt, bool givesCheck, const TranspositionTable* tt); void undo_move(Move m); void do_null_move(StateInfo& newSt, const TranspositionTable& tt); void undo_null_move(); @@ -152,7 +152,6 @@ class Position { // Accessing hash keys Key key() const; - Key key_after(Move m) const; Key material_key() const; Key pawn_key() const; Key major_piece_key() const; @@ -369,7 +368,9 @@ inline void Position::move_piece(Square from, Square to) { board[to] = pc; } -inline void Position::do_move(Move m, StateInfo& newSt) { do_move(m, newSt, gives_check(m)); } +inline void Position::do_move(Move m, StateInfo& newSt, const TranspositionTable* tt = nullptr) { + do_move(m, newSt, gives_check(m), tt); +} inline StateInfo* Position::state() const { return st; } diff --git a/src/search.cpp b/src/search.cpp index c27d93d2..7f064bfb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -872,17 +872,16 @@ Value Search::Worker::search( assert(pos.capture_stage(move)); - // Prefetch the TT entry for the resulting position - prefetch(tt.first_entry(pos.key_after(move))); + movedPiece = pos.moved_piece(move); + + pos.do_move(move, st, &tt); + thisThread->nodes.fetch_add(1, std::memory_order_relaxed); ss->currentMove = move; ss->continuationHistory = - &this->continuationHistory[ss->inCheck][true][pos.moved_piece(move)][move.to_sq()]; + &this->continuationHistory[ss->inCheck][true][movedPiece][move.to_sq()]; ss->continuationCorrectionHistory = - &this->continuationCorrectionHistory[pos.moved_piece(move)][move.to_sq()]; - - thisThread->nodes.fetch_add(1, std::memory_order_relaxed); - pos.do_move(move, st); + &this->continuationCorrectionHistory[movedPiece][move.to_sq()]; // Perform a preliminary qsearch to verify that the move holds value = -qsearch(pos, ss + 1, -probCutBeta, -probCutBeta + 1); @@ -1118,12 +1117,13 @@ moves_loop: // When in check, search starts here extension = 1; } + // Step 16. Make the move + pos.do_move(move, st, givesCheck, &tt); + thisThread->nodes.fetch_add(1, std::memory_order_relaxed); + // Add extension to new depth newDepth += extension; - // Speculative prefetch as early as possible - prefetch(tt.first_entry(pos.key_after(move))); - // Update the current move (this must be done after singular extension search) ss->currentMove = move; ss->continuationHistory = @@ -1132,10 +1132,6 @@ moves_loop: // When in check, search starts here &thisThread->continuationCorrectionHistory[movedPiece][move.to_sq()]; uint64_t nodeCount = rootNode ? uint64_t(nodes) : 0; - // Step 16. Make the move - thisThread->nodes.fetch_add(1, std::memory_order_relaxed); - pos.do_move(move, st, givesCheck); - // These reduction adjustments have proven non-linear scaling. // They are optimized to time controls of 180 + 1.8 and longer, // so changing them or adding conditions that are similar requires @@ -1637,20 +1633,19 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) continue; } - // Speculative prefetch as early as possible - prefetch(tt.first_entry(pos.key_after(move))); + // Step 7. Make and search the move + Piece movedPiece = pos.moved_piece(move); + + pos.do_move(move, st, givesCheck, &tt); + thisThread->nodes.fetch_add(1, std::memory_order_relaxed); // Update the current move ss->currentMove = move; ss->continuationHistory = - &thisThread - ->continuationHistory[ss->inCheck][capture][pos.moved_piece(move)][move.to_sq()]; + &thisThread->continuationHistory[ss->inCheck][capture][movedPiece][move.to_sq()]; ss->continuationCorrectionHistory = - &thisThread->continuationCorrectionHistory[pos.moved_piece(move)][move.to_sq()]; + &thisThread->continuationCorrectionHistory[movedPiece][move.to_sq()]; - // Step 7. Make and search the move - thisThread->nodes.fetch_add(1, std::memory_order_relaxed); - pos.do_move(move, st, givesCheck); value = -qsearch(pos, ss + 1, -beta, -alpha); pos.undo_move(move); @@ -2148,7 +2143,7 @@ bool RootMove::extract_ponder_from_tt(const TranspositionTable& tt, Position& po if (pv[0] == Move::none()) return false; - pos.do_move(pv[0], st); + pos.do_move(pv[0], st, &tt); auto [ttHit, ttData, ttWriter] = tt.probe(pos.key()); if (ttHit)