diff options
| author | Scott Gasch <[email protected]> | 2026-10-20 19:46:51 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-10-20 19:46:51 -0700 |
| commit | 6b883e83935f5e8110788565cfcb77cd6fbf8950 (patch) | |
| tree | 2ed8ca325c45edcf65bac76a20ba47e8fe8a348e /src/script.c | |
| parent | 4e6e78fc767985b9a06ce0fc5dda392027bcc25a (diff) | |
Restore 5D-alpha + 4D-beta FH% collection (improve axis + whole-node bucket), fix two bugs found via validation
Restores the full dual-table FH% collection instrumentation under
FH_TABLE_COLLECTION, which had regressed to a 4D-only alpha joint with
no beta side at all (see the prior commit's note): adds back the
`improve` axis to the alpha joint and a new 4D beta joint (depth,
gap-to-beta, own-improve, enemy-improve), both with real INC() call
sites and ALPHARAW/BETARAW/ESTRAW dump lines matching dual_table_run/'s
existing (unmodified) aggregate.py/dashboard.py/marginal_crosscheck.py
parsers exactly.
Also adds a genuine NODE-level sample into the alpha table's previously
unused late==0 bucket: "will something at this node fail high" when
iEval <= iAlpha, ground truth = the real move loop's outcome. This is
the alpha-side analog of the beta table (which only covers iEval >
iBeta) -- together they cover every null-window child's self-estimate
with no gap, motivating a follow-up (not part of this change) on
combining a parent's per-move FH% prediction with a child's own
node-level self-estimate for a sharper NODE_CUT/NODE_ALL signal than
CommonSearchInit's current purely-structural PVS alternation.
Collecting real ground truth for both new node-level samples requires
not taking the beta-side static-null-move shortcut's early `goto end`
during collection builds, same reasoning as the existing LMP disable.
Two bugs found and fixed via validation before trusting this for a
multi-day run:
1. goto-skips-initializer: Search()'s early QSearch-depth `goto end`
jumped over the new collection variables' declarations, leaving
them uninitialized (indeterminate stack garbage) on that path. Only
manifested in the release (-O3) build -- a DEBUG (-O0) build worked
by accident (the skipped stack slots happened to read as zero).
Caught via lldb on a real segfault: garbage "eligible" flags feeding
wildly out-of-range array indices. Fixed by moving the declarations
before the early goto.
2. MAX_SPLITS amplification: the new arrays were nested inside
COUNTERS.tree, which is embedded both in SEARCHER_THREAD_CONTEXT and
in split.c's g_SplitInfo[MAX_SPLITS] array -- so the ~19MB growth
got replicated 64x, inflating the binary's BSS from ~220MB to
~1.47GB and breaking valgrind's fixed-address mmap outright (harmless
at runtime for a --cpus 1 collection shard, since g_SplitInfo's
copies are never touched, but it also meant any real MP split would
have silently lost a helper thread's collected data, since no
merge-back code exists for these fields). Fixed by moving them into
a new standalone FH_COLLECTION_COUNTERS struct referenced only from
SEARCHER_THREAD_CONTEXT, not from SPLIT_INFO. BSS dropped to ~281MB;
valgrind (--track-origins=yes) now runs clean: 0 errors, 0 bytes
definitely/indirectly/possibly lost.
Validated end-to-end against real NNUE weights (--nnueweights
eval.nnue), release build, full tests/ecm_ringers.ep_ suite at sd 10:
ALPHARAW/BETARAW/ESTRAW all populated (including real late==0 "whole
node" rows), dual_table_run/'s existing aggregate.py and
marginal_crosscheck.py parse it unmodified -- depth/gap/improve axes
match exactly; the only two "mismatches" (dumb and late bucket 0) are
exactly and fully explained by the new whole-node samples, which
deliberately don't feed those two per-move marginals (hang=0/late=0
are placeholders there, not real measurements) -- not a real bug, and
reproduced byte-for-byte identical after the struct relocation above.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/script.c')
| -rw-r--r-- | src/script.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/src/script.c b/src/script.c index 7286d17..7de170c 100644 --- a/src/script.c +++ b/src/script.c @@ -97,6 +97,29 @@ typedef struct _SUITE_COUNTERS UINT64 u64TotalJointExtraTries[JOINT_EXTRA_TIER_COUNT]; UINT64 u64TotalJointExtraResearch[JOINT_EXTRA_TIER_COUNT]; UINT64 u64TotalLMPPruned; +#ifdef FH_TABLE_COLLECTION + // Full dual-table (5D alpha + 4D beta) collection -- see chess.h's + // SEARCHER_THREAD_CONTEXT.sCounters.tree comment for the full + // design. Dumped as ALPHARAW/BETARAW (5/4-axis raw cells) and + // ESTRAW (1D marginals) below, never printed as a human-readable + // table here -- same reasoning as u64TotalJointTries/FH above, + // just a much bigger shape (the 5D alpha array alone is + // 25*11*11*33*11 = 1,208,625 raw cells). + UINT64 u64TotalAlpha5Tries[SEARCH_DEPTH_BUCKETS][HANG_CP_BUCKETS][GAP_CP_BUCKETS][MOVE_NUM_BUCKETS][IMPROVE_BUCKETS]; + UINT64 u64TotalAlpha5FH[SEARCH_DEPTH_BUCKETS][HANG_CP_BUCKETS][GAP_CP_BUCKETS][MOVE_NUM_BUCKETS][IMPROVE_BUCKETS]; + UINT64 u64TotalBetaTries[SEARCH_DEPTH_BUCKETS][GAP_BETA_BUCKETS][IMPROVE_BUCKETS][IMPROVE_BUCKETS]; + UINT64 u64TotalBetaFH[SEARCH_DEPTH_BUCKETS][GAP_BETA_BUCKETS][IMPROVE_BUCKETS][IMPROVE_BUCKETS]; + UINT64 u64TotalImproveEstimateTries[IMPROVE_BUCKETS]; + UINT64 u64TotalImproveEstimateFH[IMPROVE_BUCKETS]; + UINT64 u64TotalBetaDepthEstimateTries[SEARCH_DEPTH_BUCKETS]; + UINT64 u64TotalBetaDepthEstimateFH[SEARCH_DEPTH_BUCKETS]; + UINT64 u64TotalBetaGapEstimateTries[GAP_BETA_BUCKETS]; + UINT64 u64TotalBetaGapEstimateFH[GAP_BETA_BUCKETS]; + UINT64 u64TotalBetaOwnImpEstimateTries[IMPROVE_BUCKETS]; + UINT64 u64TotalBetaOwnImpEstimateFH[IMPROVE_BUCKETS]; + UINT64 u64TotalBetaEnemyImpEstimateTries[IMPROVE_BUCKETS]; + UINT64 u64TotalBetaEnemyImpEstimateFH[IMPROVE_BUCKETS]; +#endif // FH_TABLE_COLLECTION #ifdef JOINT_EXTRA_VERIFY UINT64 u64TotalJointExtraVerifyTries[JOINT_EXTRA_TIER_COUNT][POST_REDUCE_DEPTH_BUCKETS]; UINT64 u64TotalJointExtraVerifySilentMiss[JOINT_EXTRA_TIER_COUNT][POST_REDUCE_DEPTH_BUCKETS]; @@ -506,6 +529,75 @@ _ProcessSuiteLine(char *szLine, POSITION *pos) } g_SuiteCounters.u64TotalLMPPruned += g_Options.u64LMPPruned; +#ifdef FH_TABLE_COLLECTION + { + ULONG a, b, c, d; + for (a = 0; a < SEARCH_DEPTH_BUCKETS; a++) + { + for (b = 0; b < HANG_CP_BUCKETS; b++) + { + for (c = 0; c < GAP_CP_BUCKETS; c++) + { + for (d = 0; d < MOVE_NUM_BUCKETS; d++) + { + for (v = 0; v < IMPROVE_BUCKETS; v++) + { + g_SuiteCounters.u64TotalAlpha5Tries[a][b][c][d][v] += + g_Options.u64Alpha5Tries[a][b][c][d][v]; + g_SuiteCounters.u64TotalAlpha5FH[a][b][c][d][v] += + g_Options.u64Alpha5FH[a][b][c][d][v]; + } + } + } + } + } + for (a = 0; a < SEARCH_DEPTH_BUCKETS; a++) + { + for (b = 0; b < GAP_BETA_BUCKETS; b++) + { + for (c = 0; c < IMPROVE_BUCKETS; c++) + { + for (d = 0; d < IMPROVE_BUCKETS; d++) + { + g_SuiteCounters.u64TotalBetaTries[a][b][c][d] += + g_Options.u64BetaTries[a][b][c][d]; + g_SuiteCounters.u64TotalBetaFH[a][b][c][d] += + g_Options.u64BetaFH[a][b][c][d]; + } + } + } + } + for (v = 0; v < IMPROVE_BUCKETS; v++) + { + g_SuiteCounters.u64TotalImproveEstimateTries[v] += + g_Options.u64ImproveEstimateTries[v]; + g_SuiteCounters.u64TotalImproveEstimateFH[v] += + g_Options.u64ImproveEstimateFH[v]; + g_SuiteCounters.u64TotalBetaOwnImpEstimateTries[v] += + g_Options.u64BetaOwnImpEstimateTries[v]; + g_SuiteCounters.u64TotalBetaOwnImpEstimateFH[v] += + g_Options.u64BetaOwnImpEstimateFH[v]; + g_SuiteCounters.u64TotalBetaEnemyImpEstimateTries[v] += + g_Options.u64BetaEnemyImpEstimateTries[v]; + g_SuiteCounters.u64TotalBetaEnemyImpEstimateFH[v] += + g_Options.u64BetaEnemyImpEstimateFH[v]; + } + for (v = 0; v < SEARCH_DEPTH_BUCKETS; v++) + { + g_SuiteCounters.u64TotalBetaDepthEstimateTries[v] += + g_Options.u64BetaDepthEstimateTries[v]; + g_SuiteCounters.u64TotalBetaDepthEstimateFH[v] += + g_Options.u64BetaDepthEstimateFH[v]; + } + for (v = 0; v < GAP_BETA_BUCKETS; v++) + { + g_SuiteCounters.u64TotalBetaGapEstimateTries[v] += + g_Options.u64BetaGapEstimateTries[v]; + g_SuiteCounters.u64TotalBetaGapEstimateFH[v] += + g_Options.u64BetaGapEstimateFH[v]; + } + } +#endif // FH_TABLE_COLLECTION #ifdef JOINT_EXTRA_VERIFY { ULONG w; @@ -897,6 +989,155 @@ _PrintSuiteReport(double dSuiteStart) } } +#ifdef FH_TABLE_COLLECTION + // Full dual-table raw dump for offline reassembly/baking (dual_table_run*/ + // aggregate.py et al): ALPHARAW is the 5D alpha joint (depth, dumb, + // gap, late, improve -- late==0 is the "whole node" sample, see + // chess.h), BETARAW is the 4D beta joint (depth, gap, own-improve, + // enemy-improve), ESTRAW is every independent 1D marginal (used + // only by marginal_crosscheck.py, never for baking itself). Same + // "last checkpoint per shard is authoritative" convention as the + // FOURAXISRAW dump above. + { + ULONG a, b, c, d; + for (a = 0; a < SEARCH_DEPTH_BUCKETS; a++) + { + for (b = 0; b < HANG_CP_BUCKETS; b++) + { + for (c = 0; c < GAP_CP_BUCKETS; c++) + { + for (d = 0; d < MOVE_NUM_BUCKETS; d++) + { + for (v = 0; v < IMPROVE_BUCKETS; v++) + { + if (g_SuiteCounters.u64TotalAlpha5Tries[a][b][c][d][v] > 0) + { + Trace("ALPHARAW %lu %lu %lu %lu %lu %" + COMPILER_LONGLONG_UNSIGNED_FORMAT " %" + COMPILER_LONGLONG_UNSIGNED_FORMAT "\n", + a, b, c, d, v, + g_SuiteCounters.u64TotalAlpha5FH[a][b][c][d][v], + g_SuiteCounters.u64TotalAlpha5Tries[a][b][c][d][v]); + } + } + } + } + } + } + for (a = 0; a < SEARCH_DEPTH_BUCKETS; a++) + { + for (b = 0; b < GAP_BETA_BUCKETS; b++) + { + for (c = 0; c < IMPROVE_BUCKETS; c++) + { + for (d = 0; d < IMPROVE_BUCKETS; d++) + { + if (g_SuiteCounters.u64TotalBetaTries[a][b][c][d] > 0) + { + Trace("BETARAW %lu %lu %lu %lu %" + COMPILER_LONGLONG_UNSIGNED_FORMAT " %" + COMPILER_LONGLONG_UNSIGNED_FORMAT "\n", + a, b, c, d, + g_SuiteCounters.u64TotalBetaFH[a][b][c][d], + g_SuiteCounters.u64TotalBetaTries[a][b][c][d]); + } + } + } + } + } + for (v = 0; v < SEARCH_DEPTH_BUCKETS; v++) + { + if (g_SuiteCounters.u64TotalDepthEstimateTries[v] > 0) + { + Trace("ESTRAW depth %lu %" COMPILER_LONGLONG_UNSIGNED_FORMAT + " %" COMPILER_LONGLONG_UNSIGNED_FORMAT "\n", v, + g_SuiteCounters.u64TotalDepthEstimateFH[v], + g_SuiteCounters.u64TotalDepthEstimateTries[v]); + } + } + for (v = 0; v < HANG_CP_BUCKETS; v++) + { + if (g_SuiteCounters.u64TotalHangEstimateTries[v] > 0) + { + Trace("ESTRAW dumb %lu %" COMPILER_LONGLONG_UNSIGNED_FORMAT + " %" COMPILER_LONGLONG_UNSIGNED_FORMAT "\n", v, + g_SuiteCounters.u64TotalHangEstimateFH[v], + g_SuiteCounters.u64TotalHangEstimateTries[v]); + } + } + for (v = 0; v < GAP_CP_BUCKETS; v++) + { + if (g_SuiteCounters.u64TotalGapEstimateTries[v] > 0) + { + Trace("ESTRAW gap %lu %" COMPILER_LONGLONG_UNSIGNED_FORMAT + " %" COMPILER_LONGLONG_UNSIGNED_FORMAT "\n", v, + g_SuiteCounters.u64TotalGapEstimateFH[v], + g_SuiteCounters.u64TotalGapEstimateTries[v]); + } + } + for (v = 0; v < MOVE_NUM_BUCKETS; v++) + { + if (g_SuiteCounters.u64TotalMoveNumEstimateTries[v] > 0) + { + Trace("ESTRAW late %lu %" COMPILER_LONGLONG_UNSIGNED_FORMAT + " %" COMPILER_LONGLONG_UNSIGNED_FORMAT "\n", v, + g_SuiteCounters.u64TotalMoveNumEstimateFH[v], + g_SuiteCounters.u64TotalMoveNumEstimateTries[v]); + } + } + for (v = 0; v < IMPROVE_BUCKETS; v++) + { + if (g_SuiteCounters.u64TotalImproveEstimateTries[v] > 0) + { + Trace("ESTRAW improve %lu %" COMPILER_LONGLONG_UNSIGNED_FORMAT + " %" COMPILER_LONGLONG_UNSIGNED_FORMAT "\n", v, + g_SuiteCounters.u64TotalImproveEstimateFH[v], + g_SuiteCounters.u64TotalImproveEstimateTries[v]); + } + } + for (v = 0; v < SEARCH_DEPTH_BUCKETS; v++) + { + if (g_SuiteCounters.u64TotalBetaDepthEstimateTries[v] > 0) + { + Trace("ESTRAW betadepth %lu %" COMPILER_LONGLONG_UNSIGNED_FORMAT + " %" COMPILER_LONGLONG_UNSIGNED_FORMAT "\n", v, + g_SuiteCounters.u64TotalBetaDepthEstimateFH[v], + g_SuiteCounters.u64TotalBetaDepthEstimateTries[v]); + } + } + for (v = 0; v < GAP_BETA_BUCKETS; v++) + { + if (g_SuiteCounters.u64TotalBetaGapEstimateTries[v] > 0) + { + Trace("ESTRAW betagap %lu %" COMPILER_LONGLONG_UNSIGNED_FORMAT + " %" COMPILER_LONGLONG_UNSIGNED_FORMAT "\n", v, + g_SuiteCounters.u64TotalBetaGapEstimateFH[v], + g_SuiteCounters.u64TotalBetaGapEstimateTries[v]); + } + } + for (v = 0; v < IMPROVE_BUCKETS; v++) + { + if (g_SuiteCounters.u64TotalBetaOwnImpEstimateTries[v] > 0) + { + Trace("ESTRAW betaown %lu %" COMPILER_LONGLONG_UNSIGNED_FORMAT + " %" COMPILER_LONGLONG_UNSIGNED_FORMAT "\n", v, + g_SuiteCounters.u64TotalBetaOwnImpEstimateFH[v], + g_SuiteCounters.u64TotalBetaOwnImpEstimateTries[v]); + } + } + for (v = 0; v < IMPROVE_BUCKETS; v++) + { + if (g_SuiteCounters.u64TotalBetaEnemyImpEstimateTries[v] > 0) + { + Trace("ESTRAW betaopp %lu %" COMPILER_LONGLONG_UNSIGNED_FORMAT + " %" COMPILER_LONGLONG_UNSIGNED_FORMAT "\n", v, + g_SuiteCounters.u64TotalBetaEnemyImpEstimateFH[v], + g_SuiteCounters.u64TotalBetaEnemyImpEstimateTries[v]); + } + } + } +#endif // FH_TABLE_COLLECTION + #ifdef TEST_NULL // Null-move precision/recall calibration, summed across the whole // suite (see _NullCalibBucketIndex's comment in search.c for the |
