Repository navigation
Pygin v61
v61 -- dead entries in the transposition table are now evicted first.
Material is irreversible, so a TT entry whose stored piece count exceeds the
current root's describes a position that can never occur again. It is garbage
with certainty rather than a guess, so it is taken first, while a
still-reachable old entry is depth-protected instead of clobbered on age alone.
Nothing is swept: this is purely a better choice of victim, made at store time.
Elo | +15.89 +/- 4.2 (52.29%; FIXED budget, so the magnitude is unbiased)
SPRT | none -- no early stop; post-hoc GSPRT[0,4] LLR +7.771 vs +/-2.944
Games | N: 10000 W: 2826 L: 2369 D: 4805
Penta | [146, 1044, 2226, 1375, 209] 5000 pairs, pair ratio 1.33, norm Elo +25.77
Conf | TIMED 50+0.50, x86, workers 48, Threads=1, Hash=192MB BOTH SIDES
Base | vs Old Engine/60 (same nnue_v12 net both arms), SUBSET_SEED 59
Bench | 1,140,099 nodes (unchanged -- see Known limits)
What changed
- FI-115 dead-entry TT replacement (
TT_DEADTAG, armed). The piece count
rides in 6 spare bits of the entry and is stamped whether or not the rule is
on, so with it off the store path is byte-identical to v60. - FI-113 TT prefetch now covers both cache lines of the 24-byte entry.
Node-identical, +1.09% NPS, no A/B slot spent.
Why the measurement took two attempts
The first screen accepted this at 10+0.1 with a 24 MiB table on both sides,
chosen to maximise replacement pressure. A verdict from the regime built to
favour a change does not transfer, so it was re-run at the shipped 192 MiB.
192 MiB at 10+0.1 would have measured nothing either: at 0.15s/move the table
only reaches about a 22-bit working set by ply 40, so almost nothing is evicted
and a victim rule has nothing to improve. It took the shipped size and a
clock long enough to saturate it.
Testing
testing/test_tt_deadtag.py-- randomised stress test written for this
release: a 1.5 MB table kept warm across thousands of searches so every store
is an eviction, driven by uniform-random legal positions rather than
game-derived ones. Checks inertness with the tag off, determinism with it on,
that ON actually differs from OFF, descending material against a warm table,
and 3-to-5-man endings at the bottom of the 6-bit field.
Known limits
- The bench signature and the depth-14 ladder are blind to this change by
construction -- both search from a cold table, so it never fills and the
replacement rule never fires. Do not use either to verify FI-115 is active. - Measured on x86. arm64 is unconfirmed.
- The gain is table-size dependent. At a very large
Hashthe table may never
saturate, in which case the rule rarely fires; that case is unmeasured. - Rejected alongside it: a growing TT that starts at 24 MiB and widens at
75% occupancy measured -2.88 +/- 4.4 and was dropped. Without a rehash on
each widen it discards half the live table twice mid-game. The copy-on-grow
variant is untested.
Self-contained binary: drop into any UCI GUI or lichess-bot, no Python or repo
needed. Options: Hash, Threads, OwnBook, UseTB, Move Overhead.
Full Changelog: v60...v61