Simplify out double_inc_update

Passed STC
https://tests.stockfishchess.org/tests/view/69e82e200e9667dd5a765631
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 115424 W: 29950 L: 29822 D: 55652
Ptnml(0-2): 318, 12712, 31544, 12800, 338

Passed AVX2-only STC
https://tests.stockfishchess.org/tests/view/69e953080e9667dd5a7657c0
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 134720 W: 34773 L: 34665 D: 65282
Ptnml(0-2): 344, 14958, 36694, 14974, 390

Now that our L1 size is much smaller, the overhead of tracking diffs for
`double_inc_update` is a larger share of the cost.

Interestingly ces42 measured a -0.9% slowdown locally (avxvnni build).
Meanwhile Torom and I find this branch to be 2% faster. I'm not sure exactly
what's going on but more tests may be in order

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

No functional change
This commit is contained in:
anematode
2026-04-26 09:43:42 +02:00
committed by Joost VandeVondele
parent 0e78190b72
commit 1a882efc7f
4 changed files with 29 additions and 180 deletions
-133
View File
@@ -27,7 +27,6 @@
#include "../misc.h" #include "../misc.h"
#include "../position.h" #include "../position.h"
#include "../types.h" #include "../types.h"
#include "features/half_ka_v2_hm.h"
#include "nnue_architecture.h" #include "nnue_architecture.h"
#include "nnue_common.h" #include "nnue_common.h"
#include "nnue_feature_transformer.h" // IWYU pragma: keep #include "nnue_feature_transformer.h" // IWYU pragma: keep
@@ -39,23 +38,6 @@ using namespace SIMD;
namespace { namespace {
template<IndexType TransformedFeatureDimensions>
void double_inc_update(Color perspective,
const FeatureTransformer<TransformedFeatureDimensions>& featureTransformer,
const Square ksq,
const AccumulatorState<PSQFeatureSet>& middle_state,
AccumulatorState<PSQFeatureSet>& target_state,
const AccumulatorState<PSQFeatureSet>& computed);
template<IndexType TransformedFeatureDimensions>
void double_inc_update(Color perspective,
const FeatureTransformer<TransformedFeatureDimensions>& featureTransformer,
const Square ksq,
const AccumulatorState<ThreatFeatureSet>& middle_state,
AccumulatorState<ThreatFeatureSet>& target_state,
const AccumulatorState<ThreatFeatureSet>& computed,
const DirtyPiece& dp2);
template<bool Forward, typename FeatureSet, IndexType TransformedFeatureDimensions> template<bool Forward, typename FeatureSet, IndexType TransformedFeatureDimensions>
void update_accumulator_incremental( void update_accumulator_incremental(
Color perspective, Color perspective,
@@ -213,40 +195,6 @@ void AccumulatorStack::forward_update_incremental(
for (std::size_t next = begin + 1; next < size; next++) for (std::size_t next = begin + 1; next < size; next++)
{ {
if (next + 1 < size)
{
DirtyPiece& dp1 = mut_accumulators<PSQFeatureSet>()[next].diff;
DirtyPiece& dp2 = mut_accumulators<PSQFeatureSet>()[next + 1].diff;
auto& accumulators = mut_accumulators<FeatureSet>();
if constexpr (std::is_same_v<FeatureSet, ThreatFeatureSet>)
{
if (dp2.remove_sq != SQ_NONE
&& (accumulators[next].diff.threateningSqs & square_bb(dp2.remove_sq)))
{
double_inc_update(perspective, featureTransformer, ksq, accumulators[next],
accumulators[next + 1], accumulators[next - 1], dp2);
next++;
continue;
}
}
if constexpr (std::is_same_v<FeatureSet, PSQFeatureSet>)
{
if (dp1.to != SQ_NONE && dp1.to == dp2.remove_sq)
{
const Square captureSq = dp1.to;
dp1.to = dp2.remove_sq = SQ_NONE;
double_inc_update(perspective, featureTransformer, ksq, accumulators[next],
accumulators[next + 1], accumulators[next - 1]);
dp1.to = dp2.remove_sq = captureSq;
next++;
continue;
}
}
}
update_accumulator_incremental<true>(perspective, featureTransformer, ksq, update_accumulator_incremental<true>(perspective, featureTransformer, ksq,
mut_accumulators<FeatureSet>()[next], mut_accumulators<FeatureSet>()[next],
accumulators<FeatureSet>()[next - 1]); accumulators<FeatureSet>()[next - 1]);
@@ -487,87 +435,6 @@ auto make_accumulator_update_context(Color persp
accumulatorFrom, accumulatorTo}; accumulatorFrom, accumulatorTo};
} }
template<IndexType TransformedFeatureDimensions>
void double_inc_update(Color perspective,
const FeatureTransformer<TransformedFeatureDimensions>& featureTransformer,
const Square ksq,
const AccumulatorState<PSQFeatureSet>& middle_state,
AccumulatorState<PSQFeatureSet>& target_state,
const AccumulatorState<PSQFeatureSet>& computed) {
assert(computed.acc<TransformedFeatureDimensions>().computed[perspective]);
assert(!middle_state.acc<TransformedFeatureDimensions>().computed[perspective]);
assert(!target_state.acc<TransformedFeatureDimensions>().computed[perspective]);
PSQFeatureSet::IndexList removed, added;
PSQFeatureSet::append_changed_indices(perspective, ksq, middle_state.diff, removed, added);
// you can't capture a piece that was just involved in castling since the rook ends up
// in a square that the king passed
assert(added.size() < 2);
PSQFeatureSet::append_changed_indices(perspective, ksq, target_state.diff, removed, added);
[[maybe_unused]] const int addedSize = added.ssize();
[[maybe_unused]] const int removedSize = removed.ssize();
assert(addedSize == 1);
assert(removedSize == 2 || removedSize == 3);
// Workaround compiler warning for uninitialized variables, replicated on
// profile builds on windows with gcc 14.2.0.
// Also helps with optimizations on some compilers.
sf_assume(addedSize == 1);
sf_assume(removedSize == 2 || removedSize == 3);
auto updateContext =
make_accumulator_update_context(perspective, featureTransformer, computed, target_state);
if (removedSize == 2)
{
updateContext.template apply<Add, Sub, Sub>(added[0], removed[0], removed[1]);
}
else
{
updateContext.template apply<Add, Sub, Sub, Sub>(added[0], removed[0], removed[1],
removed[2]);
}
target_state.acc<TransformedFeatureDimensions>().computed[perspective] = true;
}
template<IndexType TransformedFeatureDimensions>
void double_inc_update(Color perspective,
const FeatureTransformer<TransformedFeatureDimensions>& featureTransformer,
const Square ksq,
const AccumulatorState<ThreatFeatureSet>& middle_state,
AccumulatorState<ThreatFeatureSet>& target_state,
const AccumulatorState<ThreatFeatureSet>& computed,
const DirtyPiece& dp2) {
assert(computed.acc<TransformedFeatureDimensions>().computed[perspective]);
assert(!middle_state.acc<TransformedFeatureDimensions>().computed[perspective]);
assert(!target_state.acc<TransformedFeatureDimensions>().computed[perspective]);
ThreatFeatureSet::FusedUpdateData fusedData;
fusedData.dp2removed = dp2.remove_sq;
ThreatFeatureSet::IndexList removed, added;
const auto* pfBase = &featureTransformer.threatWeights[0];
auto pfStride = static_cast<IndexType>(TransformedFeatureDimensions);
ThreatFeatureSet::append_changed_indices(perspective, ksq, middle_state.diff, removed, added,
&fusedData, true, pfBase, pfStride);
ThreatFeatureSet::append_changed_indices(perspective, ksq, target_state.diff, removed, added,
&fusedData, false, pfBase, pfStride);
auto updateContext =
make_accumulator_update_context(perspective, featureTransformer, computed, target_state);
updateContext.apply(added, removed);
target_state.acc<TransformedFeatureDimensions>().computed[perspective] = true;
}
template<bool Forward, typename FeatureSet, IndexType TransformedFeatureDimensions> template<bool Forward, typename FeatureSet, IndexType TransformedFeatureDimensions>
void update_accumulator_incremental( void update_accumulator_incremental(
Color perspective, Color perspective,
+21 -38
View File
@@ -860,13 +860,12 @@ void Position::do_move(Move m,
Piece pc = piece_on(from); Piece pc = piece_on(from);
Piece captured = m.type_of() == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to); Piece captured = m.type_of() == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to);
dp.pc = pc; dp.pc = pc;
dp.from = from; dp.from = from;
dp.to = to; dp.to = to;
dp.add_sq = SQ_NONE; dp.add_sq = SQ_NONE;
dts.us = us; dts.us = us;
dts.prevKsq = square<KING>(us); dts.prevKsq = square<KING>(us);
dts.threatenedSqs = dts.threateningSqs = 0;
assert(color_of(pc) == us); assert(color_of(pc) == us);
assert(captured == NO_PIECE || color_of(captured) == (m.type_of() != CASTLING ? them : us)); assert(captured == NO_PIECE || color_of(captured) == (m.type_of() != CASTLING ? them : us));
@@ -1151,16 +1150,13 @@ void Position::undo_move(Move m) {
assert(pos_is_ok()); assert(pos_is_ok());
} }
template<bool PutPiece> inline void add_dirty_threat(DirtyThreats* const dts,
inline void add_dirty_threat( bool putPiece,
DirtyThreats* const dts, Piece pc, Piece threatened, Square s, Square threatenedSq) { Piece pc,
if (PutPiece) Piece threatened,
{ Square s,
dts->threatenedSqs |= threatenedSq; Square threatenedSq) {
dts->threateningSqs |= s; dts->list.push_back({pc, threatened, s, threatenedSq, putPiece});
}
dts->list.push_back({pc, threatened, s, threatenedSq, PutPiece});
} }
#ifdef USE_AVX512ICL #ifdef USE_AVX512ICL
@@ -1198,8 +1194,9 @@ void write_multiple_dirties(const Position& p,
} }
#endif #endif
template<bool PutPiece, bool ComputeRay> template<bool ComputeRay>
void Position::update_piece_threats(Piece pc, void Position::update_piece_threats(Piece pc,
bool putPiece,
Square s, Square s,
DirtyThreats* const dts, DirtyThreats* const dts,
[[maybe_unused]] Bitboard noRaysContaining) const { [[maybe_unused]] Bitboard noRaysContaining) const {
@@ -1226,11 +1223,11 @@ void Position::update_piece_threats(Piece pc,
{ {
const Square threatenedSq = lsb(discovered); const Square threatenedSq = lsb(discovered);
const Piece threatenedPc = piece_on(threatenedSq); const Piece threatenedPc = piece_on(threatenedSq);
add_dirty_threat<!PutPiece>(dts, slider, threatenedPc, sliderSq, threatenedSq); add_dirty_threat(dts, !putPiece, slider, threatenedPc, sliderSq, threatenedSq);
} }
if (addDirectAttacks) if (addDirectAttacks)
add_dirty_threat<PutPiece>(dts, slider, pc, sliderSq, s); add_dirty_threat(dts, putPiece, slider, pc, sliderSq, s);
} }
}; };
@@ -1270,27 +1267,13 @@ void Position::update_piece_threats(Piece pc,
} }
#ifdef USE_AVX512ICL #ifdef USE_AVX512ICL
if constexpr (PutPiece) DirtyThreat dt_template{pc, NO_PIECE, s, Square(0), putPiece};
{
dts->threatenedSqs |= threatened;
// A bit may only be set if that square actually produces a threat, so we
// must guard setting the square accordingly
dts->threateningSqs |= Bitboard(bool(threatened)) << s;
}
DirtyThreat dt_template{pc, NO_PIECE, s, Square(0), PutPiece};
write_multiple_dirties<DirtyThreat::ThreatenedSqOffset, DirtyThreat::ThreatenedPcOffset>( write_multiple_dirties<DirtyThreat::ThreatenedSqOffset, DirtyThreat::ThreatenedPcOffset>(
*this, threatened, dt_template, dts); *this, threatened, dt_template, dts);
Bitboard all_attackers = sliders | incoming_threats; Bitboard all_attackers = sliders | incoming_threats;
if constexpr (PutPiece) dt_template = {NO_PIECE, pc, Square(0), s, putPiece};
{
dts->threatenedSqs |= Bitboard(bool(all_attackers)) << s; // same as above
dts->threateningSqs |= all_attackers;
}
dt_template = {NO_PIECE, pc, Square(0), s, PutPiece};
write_multiple_dirties<DirtyThreat::PcSqOffset, DirtyThreat::PcOffset>(*this, all_attackers, write_multiple_dirties<DirtyThreat::PcSqOffset, DirtyThreat::PcOffset>(*this, all_attackers,
dt_template, dts); dt_template, dts);
#else #else
@@ -1302,7 +1285,7 @@ void Position::update_piece_threats(Piece pc,
assert(threatenedSq != s); assert(threatenedSq != s);
assert(threatenedPc); assert(threatenedPc);
add_dirty_threat<PutPiece>(dts, pc, threatenedPc, s, threatenedSq); add_dirty_threat(dts, putPiece, pc, threatenedPc, s, threatenedSq);
} }
#endif #endif
@@ -1328,7 +1311,7 @@ void Position::update_piece_threats(Piece pc,
assert(srcSq != s); assert(srcSq != s);
assert(srcPc != NO_PIECE); assert(srcPc != NO_PIECE);
add_dirty_threat<PutPiece>(dts, srcPc, pc, srcSq, s); add_dirty_threat(dts, putPiece, srcPc, pc, srcSq, s);
} }
#endif #endif
} }
+8 -7
View File
@@ -195,8 +195,9 @@ class Position {
void set_check_info() const; void set_check_info() const;
// Other helpers // Other helpers
template<bool PutPiece, bool ComputeRay = true> template<bool ComputeRay = true>
void update_piece_threats(Piece pc, void update_piece_threats(Piece pc,
bool putPiece,
Square s, Square s,
DirtyThreats* const dts, DirtyThreats* const dts,
Bitboard noRaysContaining = -1ULL) const; Bitboard noRaysContaining = -1ULL) const;
@@ -362,14 +363,14 @@ inline void Position::put_piece(Piece pc, Square s, DirtyThreats* const dts) {
pieceCount[make_piece(color_of(pc), ALL_PIECES)]++; pieceCount[make_piece(color_of(pc), ALL_PIECES)]++;
if (dts) if (dts)
update_piece_threats<true>(pc, s, dts); update_piece_threats(pc, true, s, dts);
} }
inline void Position::remove_piece(Square s, DirtyThreats* const dts) { inline void Position::remove_piece(Square s, DirtyThreats* const dts) {
Piece pc = board[s]; Piece pc = board[s];
if (dts) if (dts)
update_piece_threats<false>(pc, s, dts); update_piece_threats(pc, false, s, dts);
byTypeBB[ALL_PIECES] ^= s; byTypeBB[ALL_PIECES] ^= s;
byTypeBB[type_of(pc)] ^= s; byTypeBB[type_of(pc)] ^= s;
@@ -384,7 +385,7 @@ inline void Position::move_piece(Square from, Square to, DirtyThreats* const dts
Bitboard fromTo = from | to; Bitboard fromTo = from | to;
if (dts) if (dts)
update_piece_threats<false>(pc, from, dts, fromTo); update_piece_threats(pc, false, from, dts, fromTo);
byTypeBB[ALL_PIECES] ^= fromTo; byTypeBB[ALL_PIECES] ^= fromTo;
byTypeBB[type_of(pc)] ^= fromTo; byTypeBB[type_of(pc)] ^= fromTo;
@@ -393,7 +394,7 @@ inline void Position::move_piece(Square from, Square to, DirtyThreats* const dts
board[to] = pc; board[to] = pc;
if (dts) if (dts)
update_piece_threats<true>(pc, to, dts, fromTo); update_piece_threats(pc, true, to, dts, fromTo);
} }
inline void Position::swap_piece(Square s, Piece pc, DirtyThreats* const dts) { inline void Position::swap_piece(Square s, Piece pc, DirtyThreats* const dts) {
@@ -402,12 +403,12 @@ inline void Position::swap_piece(Square s, Piece pc, DirtyThreats* const dts) {
remove_piece(s); remove_piece(s);
if (dts) if (dts)
update_piece_threats<false, false>(old, s, dts); update_piece_threats<false>(old, false, s, dts);
put_piece(pc, s); put_piece(pc, s);
if (dts) if (dts)
update_piece_threats<true, false>(pc, s, dts); update_piece_threats<false>(pc, true, s, dts);
} }
inline void Position::do_move(Move m, StateInfo& newSt, const TranspositionTable* tt = nullptr) { inline void Position::do_move(Move m, StateInfo& newSt, const TranspositionTable* tt = nullptr) {
-2
View File
@@ -329,8 +329,6 @@ struct DirtyThreats {
DirtyThreatList list; DirtyThreatList list;
Color us; Color us;
Square prevKsq, ksq; Square prevKsq, ksq;
Bitboard threatenedSqs, threateningSqs;
}; };
#define ENABLE_INCR_OPERATORS_ON(T) \ #define ENABLE_INCR_OPERATORS_ON(T) \