diff options
| author | Scott Gasch <[email protected]> | 2026-10-14 13:15:31 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-10-14 13:15:31 -0700 |
| commit | ba0eac31639a3eb076ad17e8cd40f57990a53c00 (patch) | |
| tree | e50998859828f03b5cd01436a720cb46190af511 /src/test.sh | |
| parent | d91da8c2f4d16ce6beeb11f503f4041b4d8d8d54 (diff) | |
Unify HelpSearch() onto SearchOneMove (UNIFY_SEARCH.md step 4)
HelpSearch() now calls the same per-move kernel Search() uses
(searchsup.c's SearchOneMove, extracted in step 2), instead of its
own hand-rolled inline copy. This is the actual unification: helper
threads gain the real CanWeReduceOrPruneMove() gate (9 base checks +
2 move-identity exemptions) and ShouldLatePrune()'s LMP, neither of
which HelpSearch had before -- its old inline LMR-only gate was
missing the SEE-negative-capture carve-out, the mate-magnitude-alpha
check, fThreat, and both killer/countermove exemptions. It also now
gets real attack-table snapshots (bbMinorAttacksSnapshot/
bbRookAttacksSnapshot) and fThreat from g_SplitInfo (step 3's
plumbing), instead of the zeroed snapshots and missing-fThreat
degraded inputs the old inline LMR lookup used.
Per-call parameter mapping, not obvious from SearchOneMove's
search.c call site alone:
- x is chosen so (x - 1) reproduces mv's absolute index in
ctx->sMoveStack (uBegin[ctx->uPly] + uMoveNum, computed pre-MakeMove
while ctx->uPly is still the split ply) -- the same relationship
search.c's own x maintains for its callers inside the kernel.
- uLegalMoves is passed PRE-increment (g_SplitInfo[u].uAlreadyDone +
uMoveNum, not + 1): SearchOneMove itself increments it once right
after MakeMove succeeds, so passing the already-"+1" value would
double-count relative to what the old inline code's
uSplitLegalMoves computed.
- fFullWindow is always FALSE (a helper thread's first pulled move is
never this node's actual first move -- uAlreadyDone >= 1 always,
since a split only launches after the originating thread already
searched at least one move with the full window).
- fIsGeneratedMoveStage is always TRUE, fIsLeftoverMove is always
TRUE -- both already true of every move HelpSearch handles, same
as the removed inline code's own assumptions.
- MOVE_ORDERING_EVIDENCE_COLLECTION/TEST_NULL instrumentation gets
FALSE/0/throwaway-output -- confirmed with the user these
diagnostics are single-threaded-only by design, not a gap to fill.
Two real, DEBUG-only invariant breaks found and fixed getting this to
pass under real multithreading (both confirmed via a DEBUG=1 --cpus 4
stress run across all three curated suites, not just the fast smoke
test):
1. SearchOneMove's own post-UnmakeMove sanity ASSERT compares against
ctx->sPlyInfo[ctx->uPly].sPosition (pi->sPosition), which Search()
populates once at node entry -- but a helper thread never calls
Search()/CommonSearchInit for the split-point ply itself (see
eNodeType's own SPLIT_INFO comment), so that slot was stale
leftover data from whatever unrelated node the thread last helped
with. Fixed by having HelpSearch() populate it itself, once before
the move loop, matching Search()'s own convention.
2. StartParallelSearch's post-split DEBUG assert ("fail-low implies
every candidate was searched", split.c ~line 801) no longer holds
universally: a helper thread can now legitimately stop pulling
moves from a failing-low split via MOVE_KERNEL_ABORTED when
WE_SHOULD_STOP_SEARCHING's MP definition fires for a reason
unrelated to this split's own outcome (ThreadUnderTerminatingSplit
-- an ANCESTOR split up this thread's call stack terminated).
The old inline code never checked WE_SHOULD_STOP_SEARCHING
explicitly, so it never bailed early for this reason either; this
is a correctness improvement (stop grinding through a doomed
split's remaining moves once told to), not a regression, so the
assert was loosened to tolerate it rather than reverting the
early-exit.
Verified: precommit_check.sh passes. Bit-identical node counts at
sd10/--cpus 1 unaffected (confirmed again), as expected -- the
single-threaded path never calls HelpSearch(). DEBUG=1 --cpus 4 sd10
run across all three curated suites (191 positions): clean exit, zero
assertion failures, after both fixes above.
Not yet done: the solve-count/fail-high-rate comparison at --cpus 4
against head_reference/baseline_cpus4_pre_unification.md -- a single
run was taken (71/191 vs the baseline's 81/191) but --cpus 4 is
confirmed genuinely nondeterministic (real races across splits, not
noise-free), so a one-shot diff isn't a meaningful comparison either
direction. A real comparison needs match_play.py-style repeated
trials, not done here. head_reference/ itself not yet rebuilt as the
new baseline pending that.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/test.sh')
0 files changed, 0 insertions, 0 deletions
