From 676456191607e2ea6ca84d83e130bab8afdd2ea6 Mon Sep 17 00:00:00 2001 From: Timothy Herchen Date: Tue, 14 Oct 2025 05:38:44 -0700 Subject: [PATCH] Improve index generation The speedup seems to vary by machine. The indexing function can be changed w/o needing to understand intrinsics. Result of 100 runs ================== base (...ish_baseline) = 1719637 +/- 3233 test (./stockfish ) = 1734245 +/- 3534 diff = +14608 +/- 4868 speedup = +0.0085 P(speedup > 0) = 1.0000 closes https://github.com/official-stockfish/Stockfish/pull/6366 No functional change --- src/nnue/nnue_accumulator.cpp | 66 +++++++++++++++++++++-------------- src/nnue/nnue_accumulator.h | 4 +-- src/position.h | 11 +++--- 3 files changed, 48 insertions(+), 33 deletions(-) diff --git a/src/nnue/nnue_accumulator.cpp b/src/nnue/nnue_accumulator.cpp index 3096758b9..b1743329f 100644 --- a/src/nnue/nnue_accumulator.cpp +++ b/src/nnue/nnue_accumulator.cpp @@ -18,9 +18,9 @@ #include "nnue_accumulator.h" +#include #include #include -#include #include #include "../bitboard.h" @@ -362,6 +362,29 @@ void update_accumulator_incremental( (target_state.acc()).computed[Perspective] = true; } +Bitboard get_changed_pieces(const Piece old[SQUARE_NB], const Piece new_[SQUARE_NB]) { +#if defined(USE_AVX512) || defined(USE_AVX2) + static_assert(sizeof(Piece) == 1); + Bitboard same_bb = 0; + for (int i = 0; i < 64; i += 32) + { + const __m256i old_v = _mm256_loadu_si256(reinterpret_cast(old + i)); + const __m256i new_v = _mm256_loadu_si256(reinterpret_cast(new_ + i)); + const __m256i cmp_equal = _mm256_cmpeq_epi8(old_v, new_v); + const std::uint32_t equal_mask = _mm256_movemask_epi8(cmp_equal); + same_bb |= static_cast(equal_mask) << i; + } + return ~same_bb; +#else + Bitboard changed = 0; + for (Square sq = SQUARE_ZERO; sq < SQUARE_NB; ++sq) + { + changed |= static_cast(old[sq] != new_[sq]) << sq; + } + return changed; +#endif +} + template void update_accumulator_refresh_cache(const FeatureTransformer& featureTransformer, const Position& pos, @@ -374,28 +397,23 @@ void update_accumulator_refresh_cache(const FeatureTransformer& feat auto& entry = cache[ksq][Perspective]; FeatureSet::IndexList removed, added; - for (Color c : {WHITE, BLACK}) - { - for (PieceType pt = PAWN; pt <= KING; ++pt) - { - const Piece piece = make_piece(c, pt); - const Bitboard oldBB = entry.byColorBB[c] & entry.byTypeBB[pt]; - const Bitboard newBB = pos.pieces(c, pt); - Bitboard toRemove = oldBB & ~newBB; - Bitboard toAdd = newBB & ~oldBB; + const Bitboard changed_bb = get_changed_pieces(entry.pieces, pos.piece_array()); + Bitboard removed_bb = changed_bb & entry.pieceBB; + Bitboard added_bb = changed_bb & pos.pieces(); - while (toRemove) - { - Square sq = pop_lsb(toRemove); - removed.push_back(FeatureSet::make_index(sq, piece, ksq)); - } - while (toAdd) - { - Square sq = pop_lsb(toAdd); - added.push_back(FeatureSet::make_index(sq, piece, ksq)); - } - } + while (removed_bb) + { + Square sq = pop_lsb(removed_bb); + removed.push_back(FeatureSet::make_index(sq, entry.pieces[sq], ksq)); } + while (added_bb) + { + Square sq = pop_lsb(added_bb); + added.push_back(FeatureSet::make_index(sq, pos.piece_on(sq), ksq)); + } + + entry.pieceBB = pos.pieces(); + std::copy_n(pos.piece_array(), SQUARE_NB, entry.pieces); auto& accumulator = accumulatorState.acc(); accumulator.computed[Perspective] = true; @@ -518,12 +536,6 @@ void update_accumulator_refresh_cache(const FeatureTransformer& feat std::memcpy(accumulator.psqtAccumulation[Perspective], entry.psqtAccumulation, sizeof(int32_t) * PSQTBuckets); #endif - - for (Color c : {WHITE, BLACK}) - entry.byColorBB[c] = pos.pieces(c); - - for (PieceType pt = PAWN; pt <= KING; ++pt) - entry.byTypeBB[pt] = pos.pieces(pt); } } diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 10aadc917..56de0b7d0 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -71,8 +71,8 @@ struct AccumulatorCaches { struct alignas(CacheLineSize) Entry { BiasType accumulation[Size]; PSQTWeightType psqtAccumulation[PSQTBuckets]; - Bitboard byColorBB[COLOR_NB]; - Bitboard byTypeBB[PIECE_TYPE_NB]; + Piece pieces[SQUARE_NB]; + Bitboard pieceBB; // To initialize a refresh entry, we set all its bitboards empty, // so we put the biases in the accumulation, without any weights on top diff --git a/src/position.h b/src/position.h index dde496fe0..579121bf3 100644 --- a/src/position.h +++ b/src/position.h @@ -91,10 +91,11 @@ class Position { Bitboard pieces(PieceTypes... pts) const; Bitboard pieces(Color c) const; template - Bitboard pieces(Color c, PieceTypes... pts) const; - Piece piece_on(Square s) const; - Square ep_square() const; - bool empty(Square s) const; + Bitboard pieces(Color c, PieceTypes... pts) const; + Piece piece_on(Square s) const; + const Piece* piece_array() const; + Square ep_square() const; + bool empty(Square s) const; template int count(Color c) const; template @@ -208,6 +209,8 @@ inline Piece Position::piece_on(Square s) const { return board[s]; } +inline const Piece* Position::piece_array() const { return board; } + inline bool Position::empty(Square s) const { return piece_on(s) == NO_PIECE; } inline Piece Position::moved_piece(Move m) const { return piece_on(m.from_sq()); }