Skip to content

perf(magic): Read every magic at a fixed shift with the squares' tables overlapping - #404

Merged
aywrite merged 1 commit into
masterfrom
overlapping-magics
Oct 13, 2026
Merged

aywrite merged 1 commit into
masterfrom
overlapping-magics

Conversation

@aywrite

@aywrite aywrite commented Oct 13, 2026

Copy link
Copy Markdown
Owner

Every straight magic is read at twelve bits and every diagonal one at nine, so the probe shifts by a constant rather than by a count loaded from a per square array (which at x86-64-v2 goes through cl). The squares' tables overlap in one array at per square offsets, so the fixed width costs 940 KiB rather than the 2.25 MiB a block of slots a square would take (841 KiB before). The index mask stays, so the probe still carries no bounds check.

The magics and offsets are new and still built at compile time. The test side search places them greedily from the same generator and seed, and regenerate_magics reproduces the committed constants. Const evaluation checks every configuration as it fills the table.

Against f424675, nodes identical on both suites:

games suite instructions last level read misses bench instructions
fixed shift, no overlap -0.188% +3.34% -0.277%
this -0.577% -0.85% -0.786%

Misses are cachegrind's with the last level set to one core's L2 (--LL=2097152,16,64). speed.sh on a shared four core machine could not resolve it either way (-0.7%, -3.2% to +1.5%, over 60 rounds). Eight timing runs at 10+0.1 and a non-regression sprt [-10, 0] are to follow.

The release, debug, tactical and strategic suites pass, as does clippy.

🤖 Generated with Claude Code

https://claude.ai/code/session_018YMhHrVdXrSLCTea5LmtbH


Generated by Claude Code

…es overlapping

Each square's index was shifted by a count loaded from a per square
array, and at x86-64-v2 that shift goes through cl. Every straight index
is now read at twelve bits and every diagonal one at nine, so the shift
is a constant and the shift array goes.

A fixed width alone needs a block of 4,096 slots a square, 2 MiB for the
straight table, and that was measured before: fewer instructions, but
last level misses up a third. At the fixed width most of a square's
slots are empty, so here the blocks overlap. Each square starts at an
offset chosen so its slots land on slots no other square uses, or on
one holding the same attack set. The tables reach 114,947 and 5,423
entries (940 KiB, against 841 KiB before) and stay in the same power of
two arrays, so the probe keeps its mask and carries no bounds check.

The magics and offsets are new. The test side search now places them,
widest mask first, greedily, from the same generator and seed, and
const evaluation still checks every configuration as it builds the
table.

Under cachegrind, against f424675 and with the last level set to one
core's L2: the games suite -0.577% of instructions with last level read
misses -0.85%, the bench -0.786%. The fixed shift without the overlap
read -0.188%, +3.34% and -0.277%.

The clock on a shared four core machine could not resolve it either way:
+2.2% (+0.4% to +4.4%) over 15 rounds and -0.7% (-3.2% to +1.5%) over 60.
The trailer is the 60.

Bench: 11088536
Speed: -0.7% (bench nps, 95% interval -3.2% to +1.5%, 60 interleaved rounds over shuffled layouts vs f424675)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018YMhHrVdXrSLCTea5LmtbH
@aywrite
aywrite force-pushed the overlapping-magics branch from 4b6fd26 to 9e7784f Compare October 13, 2026 11:10
@github-actions

Copy link
Copy Markdown
Contributor

Speed against f4246758

Measured on AMD EPYC 7763 64-Core Processor.
Both sides built with rustc 1.99.0 (b940084d7 2026-09-28).

Both sides built and run on this runner in this job, the way
scripts/speed.sh measures a perf commit. Each round runs
both sides on a layout of its own, the same compiled code with
its code and data shuffled and moved, so the 95% interval carries
where the code landed as well as the run. The verdict holds the
interval against a 1% threshold. The default layout, the one a
release ships, is measured after as a diagnostic. The node
counts are the search's: they move when the search does, and a
speed change leaves them alone.

round     base nps  candidate nps  change
    1      4556832        4601017   +1.0%
    2      4611490        4431661   -3.9%
    3      4478110        4430498   -1.1%
    4      4512094        4417183   -2.1%
    5      4588524        4761364   +3.8%
    6      4881147        4746806   -2.8%
    7      4671233        4709657   +0.8%
    8      4663278        4798277   +2.9%
    9      4748010        4831848   +1.8%
   10      4496037        4739802   +5.4%
   11      4526943        4742324   +4.8%
   12      4678614        4798800   +2.6%
   13      4686500        4627351   -1.3%
   14      4464479        4692928   +5.1%
   15      4542743        4808942   +5.9%
   16      4659279        5041089   +8.2%
   17      4812991        4982037   +3.5%
   18      4738838        5005783   +5.6%
   19      4700804        4706267   +0.1%
   20      5011720        4764997   -4.9%
   21      5039632        4953755   -1.7%
   22      4667766        4799276   +2.8%
   23      4709921        4959489   +5.3%
   24      4861077        5020944   +3.3%
   25      4950413        5065971   +2.3%
   26      4738751        5086041   +7.3%
   27      4676023        4702784   +0.6%
   28      4857938        4669248   -3.9%
   29      4811832        4997544   +3.9%
   30      5028255        5006958   -0.4%
   31      5106561        4936997   -3.3%
   32      4999479        5105898   +2.1%
   33      4919614        5000789   +1.7%
   34      5057321        5096153   +0.8%
   35      4916493        5176570   +5.3%
   36      4930957        4883490   -1.0%
   37      4769617        5055077   +6.0%
   38      5049286        4908477   -2.8%
   39      5075567        4937593   -2.7%
   40      4945106        4790632   -3.1%

              nodes    time  median nps  faster half
base       11088536  2.34 s     4743424      4938651
candidate  11088536  2.30 s     4820395      5002625
change                            +1.6%        +1.3%

paired change +1.4%, 95% interval +0.2% to +2.6%
diagnostic, on the default layout alone +1.1%, 95% interval -1.2% to +3.0%, 13 rounds

not resolved: the interval reaches past ±1.0% without clearing it, so
more rounds are needed to say either way

Speed: +1.4% (bench nps, 95% interval +0.2% to +2.6%, 40 interleaved rounds over shuffled layouts vs f4246758)

The instructions each side's bench executed, counted under
cachegrind. The count repeats to within a few hundred
instructions, so a small change here is a real one, but it
prices instructions only: cache misses, mispredicted branches
and where the code lands are the speed's to show.

instructions against f4246758, one cachegrind run a side, bench

             instructions     nodes  per node
base       15,115,361,007  11088536    1363.2
candidate  14,997,100,425  11088536    1352.5
change             -0.78%              -0.78%

fewer instructions, beyond the ±0.7% that edits not made for speed have moved the count

@aywrite
aywrite merged commit 7256449 into master Oct 13, 2026
21 checks passed
@aywrite
aywrite deleted the overlapping-magics branch October 13, 2026 11:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants