Improve update_piece_threats

passed on avx512ICL:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 30240 W: 8026 L: 7726 D: 14488
Ptnml(0-2): 95, 3235, 8171, 3513, 106
https://tests.stockfishchess.org/tests/view/69281d9ab23dfeae38cfeeb8

passed on generic architectures:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 73184 W: 19183 L: 18821 D: 35180
Ptnml(0-2): 258, 7988, 19744, 8338, 264
https://tests.stockfishchess.org/tests/view/6928ba11b23dfeae38cff276

subsequent cleanups tested as:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 72480 W: 18678 L: 18502 D: 35300
Ptnml(0-2): 242, 7925, 19718, 8125, 230
https://tests.stockfishchess.org/tests/view/692a26adb23dfeae38cff566

We add an argument noRaysContaining, which skips all discoveries which contain
all bits in the argument; if the argument is from | to, then this will
eliminate the discovery. Separately, on AVX512ICL we can speed up the
computation of DirtyThreats by moving from a pop_lsb loop to a vector
extraction with vpcompressb. See PR for details.

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

No functional change
This commit is contained in:
Timothy Herchen
2025-12-01 17:26:50 +01:00
committed by Joost VandeVondele
parent 9e2ee13e77
commit abd835dcbc
4 changed files with 109 additions and 24 deletions
+80 -14
View File
@@ -1052,8 +1052,51 @@ inline void add_dirty_threat(
dts->list.push_back({pc, threatened, s, threatenedSq, PutPiece});
}
#ifdef USE_AVX512ICL
// Given a DirtyThreat template and bit offsets to insert the piece type and square, write the threats
// present at the given bitboard.
template<int SqShift, int PcShift>
void write_multiple_dirties(const Position& p,
Bitboard mask,
DirtyThreat dt_template,
DirtyThreats* dts) {
static_assert(sizeof(DirtyThreat) == 4);
const __m512i board = _mm512_loadu_si512(p.piece_array().data());
const __m512i AllSquares = _mm512_set_epi8(
63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41,
40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18,
17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
const int dt_count = popcount(mask);
assert(dt_count <= 16);
const __m512i template_v = _mm512_set1_epi32(dt_template.raw());
auto* write = dts->list.make_space(dt_count);
// Extract the list of squares and upconvert to 32 bits. There are never more than 16
// incoming threats so this is sufficient.
__m512i threat_squares = _mm512_maskz_compress_epi8(mask, AllSquares);
threat_squares = _mm512_cvtepi8_epi32(_mm512_castsi512_si128(threat_squares));
__m512i threat_pieces =
_mm512_maskz_permutexvar_epi8(0x1111111111111111ULL, threat_squares, board);
// Shift the piece and square into place
threat_squares = _mm512_slli_epi32(threat_squares, SqShift);
threat_pieces = _mm512_slli_epi32(threat_pieces, PcShift);
const __m512i dirties =
_mm512_ternarylogic_epi32(template_v, threat_squares, threat_pieces, 254 /* A | B | C */);
_mm512_storeu_si512(reinterpret_cast<__m512i*>(write), dirties);
}
#endif
template<bool PutPiece, bool ComputeRay>
void Position::update_piece_threats(Piece pc, Square s, DirtyThreats* const dts) {
void Position::update_piece_threats(Piece pc,
Square s,
DirtyThreats* const dts,
Bitboard noRaysContaining) {
const Bitboard occupied = pieces();
const Bitboard rookQueens = pieces(ROOK, QUEEN);
const Bitboard bishopQueens = pieces(BISHOP, QUEEN);
@@ -1093,7 +1136,36 @@ void Position::update_piece_threats(Piece pc, Square s, DirtyThreats* const dts)
}
threatened &= occupied;
Bitboard sliders = (rookQueens & rAttacks) | (bishopQueens & bAttacks);
Bitboard incoming_threats =
(PseudoAttacks[KNIGHT][s] & knights) | (attacks_bb<PAWN>(s, WHITE) & blackPawns)
| (attacks_bb<PAWN>(s, BLACK) & whitePawns) | (PseudoAttacks[KING][s] & kings);
#ifdef USE_AVX512ICL
if (threatened)
{
if constexpr (PutPiece)
{
dts->threatenedSqs |= threatened;
dts->threateningSqs |= square_bb(s);
}
DirtyThreat dt_template{pc, NO_PIECE, s, Square(0), PutPiece};
write_multiple_dirties<DirtyThreat::ThreatenedSqOffset, DirtyThreat::ThreatenedPcOffset>(
*this, threatened, dt_template, dts);
}
Bitboard all_attackers = sliders | incoming_threats;
if (!all_attackers)
return; // Square s is threatened iff there's at least one attacker
dts->threatenedSqs |= square_bb(s);
dts->threateningSqs |= all_attackers;
DirtyThreat dt_template{NO_PIECE, pc, Square(0), s, PutPiece};
write_multiple_dirties<DirtyThreat::PcSqOffset, DirtyThreat::PcOffset>(*this, all_attackers,
dt_template, dts);
#else
while (threatened)
{
Square threatenedSq = pop_lsb(threatened);
@@ -1104,8 +1176,7 @@ void Position::update_piece_threats(Piece pc, Square s, DirtyThreats* const dts)
add_dirty_threat<PutPiece>(dts, pc, threatenedPc, s, threatenedSq);
}
Bitboard sliders = (rookQueens & rAttacks) | (bishopQueens & bAttacks);
#endif
if constexpr (ComputeRay)
{
@@ -1118,30 +1189,24 @@ void Position::update_piece_threats(Piece pc, Square s, DirtyThreats* const dts)
const Bitboard discovered = ray & qAttacks & occupied;
assert(!more_than_one(discovered));
if (discovered)
if (discovered && (RayPassBB[sliderSq][s] & noRaysContaining) != noRaysContaining)
{
const Square threatenedSq = lsb(discovered);
const Piece threatenedPc = piece_on(threatenedSq);
add_dirty_threat<!PutPiece>(dts, slider, threatenedPc, sliderSq, threatenedSq);
}
#ifndef USE_AVX512ICL // for ICL, direct threats were processed earlier (all_attackers)
add_dirty_threat<PutPiece>(dts, slider, pc, sliderSq, s);
#endif
}
}
else
{
while (sliders)
{
Square sliderSq = pop_lsb(sliders);
Piece slider = piece_on(sliderSq);
add_dirty_threat<PutPiece>(dts, slider, pc, sliderSq, s);
}
incoming_threats |= sliders;
}
Bitboard incoming_threats =
(PseudoAttacks[KNIGHT][s] & knights) | (attacks_bb<PAWN>(s, WHITE) & blackPawns)
| (attacks_bb<PAWN>(s, BLACK) & whitePawns) | (PseudoAttacks[KING][s] & kings);
#ifndef USE_AVX512ICL
while (incoming_threats)
{
Square srcSq = pop_lsb(incoming_threats);
@@ -1152,6 +1217,7 @@ void Position::update_piece_threats(Piece pc, Square s, DirtyThreats* const dts)
add_dirty_threat<PutPiece>(dts, srcPc, pc, srcSq, s);
}
#endif
}
// Helper used to do/undo a castling move. This is a bit