Repository navigation
v1.0.0 "Herd"
The first complete, multi threaded Elephant. Elephants move as a herd, so do the search threads now: they share one
transposition table and spread out over different depths instead of all doing the same work. 1.0 also means every
advertised UCI option works and every valid UCI command gets an answer.
On four threads 1.0 is about +200 Elo over 0.15.2 on one. On a single thread it plays like 0.15.2, this cycle was
about correctness and robustness, the next one is about strength again.
Transposition table
- Buckets of 8 entries, one 64 byte cache line, a probe reads a single line. Each entry is one 64-bit word written and
read atomically, threads sharing the table never see half an entry. - Twice the entries per megabyte, and a new result always finds a slot, the least valuable entry in the bucket makes
room. The old table dropped shallower results, exact scores too, once a slot held a deeper entry from the same search. - A fail-low no longer wipes the stored move.
- Every megabyte of
Hashis used,Hash 100was 64 MB before. The maximum goes from 1 GB to 32 GB, and the table is
cleared with all threads. info hashfull.
Lazy SMP
- Thread 0 is the main thread, it reports, manages time and its move is played. It stops the helpers when it's done.
- Helpers skip iterations in staggered blocks, they search ahead of the main thread and fill the table with deeper
results. info nodesandgo nodescount every thread.debug onprints what the helper threads search as info strings.
UCI
ucinewgameclears the transposition table, games no longer influence each other.go nodes,go mateandgo searchmoveswork, they used to stop the engine without a bestmove.- A malformed command reports an
info string errorand the engine keeps running. Threads1–64,Hash1–32768.
Fixes
- Threefold repetitions after a double pawn push were missed: the en passant square was set and hashed even when no
pawn could capture, so the same position reached two ways hashed differently. fastchess reported "PV continues after
threefold repetition". - A FEN with black to move was hashed without the side to move key, the same position reached by moves hashed
differently (#13). - A non-pawn move onto the en passant square was flagged as an en passant capture (#14).
- The fifty move counter wasn't reset by a pawn push (#15).
- The bench measured mostly the OS mapping in a new 64 MB table for every position, it now measures the search.
Under the hood
- The legacy move generator is gone, the staged tusk generator the search has used since 0.14 is the only one. The
move generator tests stay parameterized on the generator, ready to A/B a new one. - Builds with assertions check at every node that the incremental hash equals a full recompute.
- Two test tiers: a smoke tier, unit tests, perft, bench and node-limited EPD suites (WAC, STS) in about a minute
before every merge, and an extended tier (WAC, STS, Arasan 2024 with 1 and 4 threads) in CI. Every suite has a
baseline that warns and a minimum that fails. EPD files with comments containing=, coordinate notation in STS
score lists and lines missing a comma are read correctly.
OpenBench
1.0.0 on 4 threads vs 0.15.2 on 1 thread
Elo | 197.63 +- 26.49 (95%)
SPRT | 8.0+0.08s Threads=4 Hash=32MB
LLR | 2.98 (-2.94, 2.94) [0.00, 5.00]
Games | N: 414 W: 233 L: 20 D: 161
Penta | [0, 4, 49, 91, 63]
1.0.0 vs 0.15.2, 1 thread. Stopped, the gainer bounds weren't going to pass: at this time control and hash the
two play alike.
Elo | -2.22 +- 5.41 (95%)
SPRT | 8.0+0.08s Threads=1 Hash=16MB
LLR | -1.75 (-2.94, 2.94) [0.00, 5.00]
Games | N: 7362 W: 1785 L: 1832 D: 3745
Penta | [212, 906, 1471, 901, 191]
The changes on their own. The first two ran while OpenBench scaled the time control by about 5x, an 8+0.08 test
played close to 42+0.42 (fixed during the cycle, the bench now measures the search). The transposition table work
shows at longer time controls and small hash, where the table fills up; at a real 8+0.08 with 16 MB it barely does.
herd-tt-clusters vs 0.15.2, non-regression (effectively about 42+0.42)
Elo | 40.45 +- 11.72 (95%)
SPRT | 8.0+0.08s Threads=1 Hash=8MB
LLR | 2.96 (-2.94, 2.94) [-3.00, 1.00]
Games | N: 1458 W: 408 L: 239 D: 811
Penta | [19, 128, 300, 229, 53]
herd-lazy-smp vs herd, 4 threads each (effectively about 42+0.42)
Elo | 34.91 +- 11.62 (95%)
SPRT | 8.0+0.08s Threads=4 Hash=32MB
LLR | 2.96 (-2.94, 2.94) [0.00, 5.00]
Games | N: 1378 W: 352 L: 214 D: 812
Penta | [16, 122, 302, 206, 43]
herd-tt-polish vs herd, non-regression. Stopped undecided, merged: the search is the same, node for node.
Elo | -2.83 +- 10.91 (95%)
SPRT | 8.0+0.08s Threads=1 Hash=24MB
LLR | -0.18 (-2.94, 2.94) [-3.00, 1.00]
Games | N: 1596 W: 320 L: 333 D: 943
Penta | [36, 193, 348, 190, 31]
herd-ep-repetition vs herd, non-regression. Stopped undecided, merged as a correctness fix.
Elo | -3.76 +- 6.60 (95%)
SPRT | 8.0+0.08s Threads=1 Hash=8MB
LLR | -0.83 (-2.94, 2.94) [-7.00, 1.00]
Games | N: 4984 W: 1186 L: 1240 D: 2558
Penta | [146, 629, 975, 617, 125]
Speed
Perft 6 from the start position about 140M moves/s, search from the start position about 4.5M nps, bench 503240
nodes at 4.4M nps. One thread, the g++ build OpenBench uses (-O3 -march=native), AMD Ryzen 9 9900X.