summaryrefslogtreecommitdiff
path: root/src/search.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-09-16 08:02:21 -0700
committerScott Gasch <[email protected]>2026-09-16 08:02:21 -0700
commit966a0ef4da9cf3911eb3da0533179199808717b4 (patch)
treee09363b574215ef0d8996155a5ab24314db2dd69 /src/search.c
parentb91c8453f3fe21c8ccd3568923d8e39141c9c945 (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.c33
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