Speedup movegen with VBMI2

Passed STC
LLR: 2.96 (-2.94,2.94) <0.00,2.00>
Total: 166720 W: 43191 L: 42701 D: 80828
Ptnml(0-2): 348, 18567, 45069, 18999, 377
https://tests.stockfishchess.org/tests/view/686ae98dfe0f2fe354c0c867

Refactor movegen to emit to a vector with 16-bit elements, which enables a
speedup with AVX512-VBMI2 when writing moves to the move list.

Very crude timing measurements of perft via timing ./stockfish "go perft 7"
demonstrates approximately 17% perft speedup:

Summary
  ./Stockfish-dev/src/stockfish 'go perft 7' ran
    1.17 ± 0.04 times faster than ./Stockfish-base/src/stockfish 'go perft 7'

Estimated overall nps increase of 0.4% via speedtest: 33605229 -> 33749825
(many thanks JonathanHallstrom).

The corresponding arch is avx512icl as it is a good baseline for consumer
avx-512 feature set support; Intel Ice Lake was the first consumer AVX-512 CPU
and it is a decent subset of what AMD Zen 4 supports.

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

No functional change
This commit is contained in:
87
2025-07-24 10:08:35 +02:00
committed by Joost VandeVondele
parent c13f883dc4
commit 8c2d21f91a
8 changed files with 156 additions and 60 deletions
+20 -10
View File
@@ -123,7 +123,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, int th, const CapturePieceTo
// Captures are ordered by Most Valuable Victim (MVV), preferring captures
// with a good history. Quiets moves are ordered using the history tables.
template<GenType Type>
void MovePicker::score() {
ExtMove* MovePicker::score(MoveList<Type>& ml) {
static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
@@ -138,8 +138,12 @@ void MovePicker::score() {
threatByLesser[QUEEN] = pos.attacks_by<ROOK>(~us) | threatByLesser[ROOK];
}
for (auto& m : *this)
ExtMove* it = cur;
for (auto move : ml)
{
ExtMove& m = *it++;
m = move;
const Square from = m.from_sq();
const Square to = m.to_sq();
const Piece pc = pos.moved_piece(m);
@@ -189,6 +193,7 @@ void MovePicker::score() {
}
}
}
return it;
}
// Returns the next move satisfying a predicate function.
@@ -222,14 +227,16 @@ top:
case CAPTURE_INIT :
case PROBCUT_INIT :
case QCAPTURE_INIT :
case QCAPTURE_INIT : {
MoveList<CAPTURES> ml(pos);
cur = endBadCaptures = moves;
endCur = endCaptures = generate<CAPTURES>(pos, cur);
endCur = endCaptures = score<CAPTURES>(ml);
score<CAPTURES>();
partial_insertion_sort(cur, endCur, std::numeric_limits<int>::min());
++stage;
goto top;
}
case GOOD_CAPTURE :
if (select([&]() {
@@ -246,9 +253,10 @@ top:
case QUIET_INIT :
if (!skipQuiets)
{
endCur = endGenerated = generate<QUIETS>(pos, cur);
MoveList<QUIETS> ml(pos);
endCur = endGenerated = score<QUIETS>(ml);
score<QUIETS>();
partial_insertion_sort(cur, endCur, -3560 * depth);
}
@@ -283,14 +291,16 @@ top:
return Move::none();
case EVASION_INIT :
case EVASION_INIT : {
MoveList<EVASIONS> ml(pos);
cur = moves;
endCur = endGenerated = generate<EVASIONS>(pos, cur);
endCur = endGenerated = score<EVASIONS>(ml);
score<EVASIONS>();
partial_insertion_sort(cur, endCur, std::numeric_limits<int>::min());
++stage;
[[fallthrough]];
}
case EVASION :
case QCAPTURE :