From d173a0655d04b95497eefb75b400baa3eff56f93 Mon Sep 17 00:00:00 2001 From: anematode Date: Wed, 18 Mar 2026 20:48:03 +0100 Subject: [PATCH] Speedup splat_moves on avx512icl STC ``` LLR: 5.45 (-2.94,2.94) <0.00,2.00> Total: 612192 W: 158602 L: 157260 D: 296330 Ptnml(0-2): 1634, 67783, 166027, 68911, 1741 ``` @Torom measured: ``` sf_base = 2380902 +/- 1016 (95%) sf_test = 2391426 +/- 1065 (95%) diff = 10524 +/- 1336 (95%) speedup = 0.44203% +/- 0.056% (95%) ``` I've verified that `_mm512_slli_epi16(v, 0)` gets optimized out in GCC 9+ and clang 4+. Added constants to types.h so that people messing around with the layout of `Move` don't have to know about this part of the code. closes https://github.com/official-stockfish/Stockfish/pull/6670 No functional change --- src/movegen.cpp | 58 ++++++++++++++++++------------------------------- src/search.cpp | 5 +++-- src/types.h | 3 +++ 3 files changed, 27 insertions(+), 39 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index e63707a1f..7ba5bf2c0 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -36,53 +36,37 @@ namespace { #if defined(USE_AVX512ICL) -inline Move* write_moves(Move* moveList, uint32_t mask, __m512i vector) { - // Avoid _mm512_mask_compressstoreu_epi16() as it's 256 uOps on Zen4 - _mm512_storeu_si512(reinterpret_cast<__m512i*>(moveList), - _mm512_maskz_compress_epi16(mask, vector)); - return moveList + popcount(mask); -} +// clang-format off +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); +// clang-format on template inline Move* splat_pawn_moves(Move* moveList, Bitboard to_bb) { - alignas(64) static constexpr auto SPLAT_TABLE = [] { - std::array table{}; - for (int i = 0; i < 64; i++) - { - Square from{uint8_t(std::clamp(i - offset, 0, 63))}; - table[i] = {Move(from, Square{uint8_t(i)})}; - } - return table; - }(); + assert(popcount(to_bb) <= 8); // <= 8 pawns per side - auto table = reinterpret_cast(SPLAT_TABLE.data()); + const __m128i toSquares = + _mm_cvtepi8_epi16(_mm512_castsi512_si128(_mm512_maskz_compress_epi8(to_bb, AllSquares))); + const __m128i fromSquares = _mm_subs_epi16(toSquares, _mm_set1_epi16(offset)); + const __m128i moves = _mm_or_si128(_mm_slli_epi16(fromSquares, Move::FromSqShift), + _mm_slli_epi16(toSquares, Move::ToSqShift)); - moveList = - write_moves(moveList, static_cast(to_bb >> 0), _mm512_load_si512(table + 0)); - moveList = - write_moves(moveList, static_cast(to_bb >> 32), _mm512_load_si512(table + 1)); - - return moveList; + _mm_storeu_si128(reinterpret_cast<__m128i*>(moveList), moves); + return moveList + popcount(to_bb); } inline Move* splat_moves(Move* moveList, Square from, Bitboard to_bb) { - alignas(64) static constexpr auto SPLAT_TABLE = [] { - std::array table{}; - for (uint8_t i = 0; i < 64; i++) - table[i] = {Move(SQUARE_ZERO, Square{i})}; - return table; - }(); + assert(popcount(to_bb) <= 32); // Q can attack up to 27 squares - __m512i fromVec = _mm512_set1_epi16(Move(from, SQUARE_ZERO).raw()); + const __m512i fromVec = _mm512_set1_epi16(Move(from, SQUARE_ZERO).raw()); + const __m512i toSquares = + _mm512_cvtepi8_epi16(_mm512_castsi512_si256(_mm512_maskz_compress_epi8(to_bb, AllSquares))); + const __m512i moves = _mm512_or_si512(fromVec, _mm512_slli_epi16(toSquares, Move::ToSqShift)); - auto table = reinterpret_cast(SPLAT_TABLE.data()); - - moveList = write_moves(moveList, static_cast(to_bb >> 0), - _mm512_or_si512(_mm512_load_si512(table + 0), fromVec)); - moveList = write_moves(moveList, static_cast(to_bb >> 32), - _mm512_or_si512(_mm512_load_si512(table + 1), fromVec)); - - return moveList; + _mm512_storeu_si512(moveList, moves); + return moveList + popcount(to_bb); } #else diff --git a/src/search.cpp b/src/search.cpp index f69fc29fe..2def21d02 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1495,8 +1495,9 @@ moves_loop: // When in check, search starts here if (!ss->inCheck && !(bestMove && pos.capture(bestMove)) && (bestValue > ss->staticEval) == bool(bestMove)) { - auto bonus = std::clamp(int(bestValue - ss->staticEval) * depth * (bestMove ? 12 : 17) / 128, - -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); + auto bonus = + std::clamp(int(bestValue - ss->staticEval) * depth * (bestMove ? 12 : 17) / 128, + -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); update_correction_history(pos, ss, *this, 1069 * bonus / 1024); } diff --git a/src/types.h b/src/types.h index bfaa658e9..23543e478 100644 --- a/src/types.h +++ b/src/types.h @@ -473,6 +473,9 @@ class Move { std::size_t operator()(const Move& m) const { return make_key(m.data); } }; + static constexpr int FromSqShift = 6; + static constexpr int ToSqShift = 0; + protected: std::uint16_t data; };