diff options
| author | Scott Gasch <[email protected]> | 2026-09-18 23:43:55 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-09-18 23:43:55 -0700 |
| commit | b70be6fde0fef0694e404e16d70f00c73dd971a2 (patch) | |
| tree | d5523636bf76bd53f8ac4a60e508c458fa3c2a51 /src/testsee.c | |
| parent | 004a5736c6d41747d8fc0e15d230ab80bd6cced6 (diff) | |
see.c: replace _WhoAttacksSquareBB's ray-walk with magic-bitboard lookups
_RookAttacksBB/_BishopAttacksBB (generate.c) are already O(1)
FORCEINLINE magic-table lookups, proven and already used everywhere
else in this codebase (eval.c, searchsup.c, generate.c's own move
generation) -- this function alone predated that migration and was
left on a manual per-direction ray-walk (4 rook + 4 bishop directions,
nearest-blocker isolation via FastFirstBit/FastLastBit). King handling
is also branchless now: king adjacency is symmetric, so
g_KingAttacksBB[cSquare] doubles as "every square a king standing
there would attack cSquare from," same O(1)-table-lookup shape the
knight line already uses -- no DISTANCE()==1 branch needed.
Old ray-walk kept as _WhoAttacksSquareRayBB, testsee.c's
TestWhoAttacksSquareMagic correctness-diff reference (20000 random
positions x every square x both colors, clean) -- same convention as
_AddXRaysMailbox next to _AddXRaysBB from an earlier SEE investigation
this session.
Isolated benchmark: magic is 0.95x-0.99x ray-walk in a standalone
3-position microbenchmark (small, since the ray-walk already has its
own early-out for "no slider anywhere near this square," per its own
header comment). Measured end-to-end (sd10 + sn5M, all three curated
suites, before vs. after): node counts and solve counts bit-for-bit
identical (pure algorithmic swap, zero behavior change). Wall-clock
~0.8% faster overall, all 6 measurements faster, none slower.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/testsee.c')
| -rw-r--r-- | src/testsee.c | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/src/testsee.c b/src/testsee.c index 6346697..bbcd7e0 100644 --- a/src/testsee.c +++ b/src/testsee.c @@ -519,4 +519,109 @@ TestSeeFastSign(void) free(ctx2); } } + +// _WhoAttacksSquareMagicBB (see.c) replaces _WhoAttacksSquareBB's +// manual per-direction ray-walk with the same O(1) magic-bitboard +// lookups (_RookAttacksBB/_BishopAttacksBB, generate.c) already used +// everywhere else in this codebase. Same correctness+speed pattern as +// the other Test* functions here. +void +TestWhoAttacksSquareMagic(void) +{ + POSITION pos; + ULONG u; + COOR c; + ULONG uSide; + BITBOARD bbRay; + BITBOARD bbMagic; + const ULONG uPositions = 20000; + + Trace("Testing _WhoAttacksSquareMagicBB (magic vs ray-walk)...\n"); + for (u = 0; u < uPositions; u++) + { + GenerateRandomLegalPosition(&pos); + FOREACH_SQUARE(c) + { + if (!IS_ON_BOARD(c)) continue; + for (uSide = BLACK; uSide <= WHITE; uSide++) + { + bbRay = _WhoAttacksSquareRayBB(&pos, c, uSide, pos.bbOccupied); + bbMagic = _WhoAttacksSquareBB(&pos, c, uSide, + pos.bbOccupied); + if (bbRay != bbMagic) + { + Trace("cSquare=%d uSide=%lu bbRay=0x%llx bbMagic=0x%llx\n", + c, uSide, bbRay, bbMagic); + UtilPanic(TESTCASE_FAILURE, + &pos, + "_WhoAttacksSquareBB/ray-walk mismatch", + NULL, NULL, + __FILE__, __LINE__); + } + } + } + } + + // + // Speed: same isolated, interleaved cycles/call methodology as the + // other benchmarks above. + // + { + static const char *rgszFen[3] = + { + "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", + "r1bq1rk1/pp2bppp/2n1pn2/2pp4/3P4/2NBPN2/PP3PPP/R1BQ1RK1 w - - 0 1", + "8/5k2/8/3K4/8/8/8/4R3 w - - 0 1", + }; + static const char *rgszLabel[3] = + { + "opening ", "middlegame", "endgame ", + }; + POSITION posBench; + BITBOARD bbSink; + UINT64 u64RayTotal, u64MagicTotal, u64Start; + ULONG uIter; + ULONG uSq; + COOR cBench; + ULONG uSideBench; + const ULONG uCallsPerPosition = 200000; + + Trace("Benchmarking _WhoAttacksSquareBB: ray-walk vs magic " + "(interleaved, %lu calls/position)...\n", + uCallsPerPosition); + for (u = 0; u < 3; u++) + { + FenToPosition(&posBench, (char *)rgszFen[u]); + u64RayTotal = 0; + u64MagicTotal = 0; + for (uIter = 0; uIter < uCallsPerPosition; uIter++) + { + uSq = uIter % 64; + cBench = BIT_NUMBER_TO_COOR(uSq); + uSideBench = uIter & 1; + if (!IS_ON_BOARD(cBench)) continue; + + u64Start = SystemReadTimeStampCounter(); + bbSink = _WhoAttacksSquareRayBB(&posBench, cBench, uSideBench, + posBench.bbOccupied); + u64RayTotal += (SystemReadTimeStampCounter() - u64Start); + + u64Start = SystemReadTimeStampCounter(); + bbSink = _WhoAttacksSquareBB(&posBench, cBench, + uSideBench, + posBench.bbOccupied); + u64MagicTotal += (SystemReadTimeStampCounter() - u64Start); + (void)bbSink; + } + printf(" %s: ray-walk %" COMPILER_LONGLONG_UNSIGNED_FORMAT + " cycles/call, magic %" + COMPILER_LONGLONG_UNSIGNED_FORMAT " cycles/call " + "(magic is %.2fx ray-walk)\n", + rgszLabel[u], + u64RayTotal / uCallsPerPosition, + u64MagicTotal / uCallsPerPosition, + (double)u64MagicTotal / (double)u64RayTotal); + } + } +} #endif // TEST |
