diff options
| author | Scott Gasch <[email protected]> | 2026-09-16 08:02:21 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-09-16 08:02:21 -0700 |
| commit | 966a0ef4da9cf3911eb3da0533179199808717b4 (patch) | |
| tree | e09363b574215ef0d8996155a5ab24314db2dd69 /src/search.c | |
| parent | b91c8453f3fe21c8ccd3568923d8e39141c9c945 (diff) | |
Retire cPawns/cNonPawns piece-location lists in favor of bbPawns/bbPieces/cKingSq, and hoist two redundant TotalNonPawnCount POPCNT calls out of per-node hot loops.
POSITION no longer stores cPawns[2][8], cNonPawns[2][16], uPawnCount[2],
uNonPawnCount[2][8], or uWhiteSqBishopCount[2] -- every reader now goes
through PawnMaterial/TotalNonPawnCount/WhiteSqBishopCount/KingPosition
(chess.h), computed from the already-incrementally-maintained bbPawns/
bbPieces/cKingSq bitboards via a single -mpopcnt POPCNT instead of a
stored counter. move.c's make/unmake loses the list-swap-on-removal
bookkeeping entirely. GNUmakefile now passes -mpopcnt so the popcount
builtins actually compile to one instruction instead of clang's ~15-op
SWAR fallback.
Measured a small but real nps regression from the naive swap (search.c's
QSearch move-filter and eval.c's _EvalKing were each recomputing the same
TotalNonPawnCount sum on every candidate move / twice per node); hoisted
both to a single call per node instead. Net result across a same-code
self-play match: ~1541.5 knps candidate vs ~1505.7 knps head_reference.
A third hoist (FindEnprisePiece out of SelectBestWithHistory) was tried
and reverted -- its cache isn't actually loop-invariant within a node
(populated from inside Eval(), which doesn't run before every selection
call), caught by a DEBUG-build ASSERT before it landed.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/search.c')
| -rw-r--r-- | src/search.c | 33 |
1 files changed, 29 insertions, 4 deletions
diff --git a/src/search.c b/src/search.c index 573f78c..91e3b2b 100644 --- a/src/search.c +++ b/src/search.c @@ -1450,7 +1450,21 @@ static FLAG INLINE _ShouldWeConsiderThisMove(IN SEARCHER_THREAD_CONTEXT *ctx, IN ULONG uMoveNum, IN SCORE iFutility, - IN FLAG fGeneratedChecks + IN FLAG fGeneratedChecks, + // TotalNonPawnCount(&ctx->sPosition, + // FLIP(ctx->sPosition.uToMove)) -- every + // capture's pCaptured is necessarily the + // opponent's piece, and uToMove doesn't + // change across the move loop this is called + // from (search.c's QSearch), so this is one + // value for the whole loop. Hoisted out to + // the caller instead of recomputed (a POPCNT + // each) on every candidate move -- see + // 2026-09-16 chess.h/search.c history for why + // this replaced a per-call cache-with-valid- + // flag branch that would have cost more than + // it saved. + IN ULONG uOpponentNonPawnCount #ifdef CALIBRATE_QSEARCH_FUTILITY , IN SCORE iAlpha , IN SCORE iBeta @@ -1517,10 +1531,13 @@ _ShouldWeConsiderThisMove(IN SEARCHER_THREAD_CONTEXT *ctx, // might save the game... uColor = GET_COLOR(mv.pCaptured); ASSERT(OPPOSITE_COLORS(ctx->sPosition.uToMove, uColor)); + ASSERT(uColor == FLIP(ctx->sPosition.uToMove)); + ASSERT(uOpponentNonPawnCount == + TotalNonPawnCount(&ctx->sPosition, uColor)); if ((IS_PAWN(mv.pCaptured) && - ctx->sPosition.uPawnCount[uColor] == 1) || + FastCountBits(ctx->sPosition.bbPawns[uColor]) == 1) || (!IS_PAWN(mv.pCaptured) && - ctx->sPosition.uNonPawnCount[uColor][0] == 2)) + uOpponentNonPawnCount == 2)) { return(TRUE); } @@ -1923,6 +1940,7 @@ QSearch(IN SEARCHER_THREAD_CONTEXT *ctx, SCORE iFutility; SCORE rgiPositional[2]; ULONG x; + ULONG uOpponentNonPawnCount; #ifdef PERF_COUNTERS ULONG uLegalMoves; #endif @@ -2092,6 +2110,12 @@ QSearch(IN SEARCHER_THREAD_CONTEXT *ctx, ); GenerateMoves(ctx, NULLMOVE, _WhatToGen[fIncludeChecks]); + // Hoisted out of _ShouldWeConsiderThisMove's per-move loop below: + // pos->uToMove is fixed for the whole loop, so the opponent's + // TotalNonPawnCount is one POPCNT for the entire node, not one per + // candidate move. + uOpponentNonPawnCount = TotalNonPawnCount(pos, FLIP(pos->uToMove)); + #ifdef PERF_COUNTERS uLegalMoves = 0; #endif @@ -2118,7 +2142,8 @@ QSearch(IN SEARCHER_THREAD_CONTEXT *ctx, if (FALSE == _ShouldWeConsiderThisMove(ctx, x, iFutility, - fIncludeChecks + fIncludeChecks, + uOpponentNonPawnCount #ifdef CALIBRATE_QSEARCH_FUTILITY , iAlpha , iBeta |
