diff options
| author | Scott Gasch <[email protected]> | 2026-10-25 10:39:58 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-10-25 10:39:58 -0700 |
| commit | f97b2710981e936cfb500ad0cdf1ceab233efad8 (patch) | |
| tree | 0004275d9dae8f7df4b5f86f3b750d80bc3f4a52 /src/testgenerate.c | |
| parent | 3927a4cac6ec254a8e34a5e864300f75c3efc9f3 (diff) | |
Make bitboard move generation unconditional; delete the mailbox fallback
All GENERATE_*_BITBOARD/EXPOSESCHECK_BITBOARD/ISATTACKED_BITBOARD/
UNBLOCKED_BITBOARD flags were already default-on (ifndef
DISABLE_BITBOARD_MOVEGEN); this removes that opt-out entirely (GNUmakefile)
and deletes the mailbox-era code the flags used to toggle between, since
there's no longer a build configuration that still needs it:
- generate.c: the mailbox per-piece generators and escape-generators
(GenerateKnight/GenerateWhiteKnight/GenerateBishop/GenerateRook/
GenerateQueen/GenerateBlackKing/GenerateWhiteKing/GenerateWhitePawn/
GenerateBlackPawn and their SaveMe* escape-generator counterparts),
_FindUnblockedSquares and its DEBUG-build cross-check inside
_FindUnblockedSquaresBB, and the dispatch-table #if/#else pairs that
chose between mailbox and bitboard functions (now unconditionally
bitboard). WouldGiveCheck simplified to its UNBLOCKED_BITBOARD-only
body. g_iBDeltas/g_iRDeltas (orphaned once their only consumers were
deleted) removed; g_iNDeltas (still used by data.c's InitMagic) moved
there, next to its real consumer.
- movesup.c: mailbox FasterExposesCheck/ExposesCheck/ExposesCheckEp/
IsAttacked/InCheck deleted; chess.h's three-way macro choice
simplified to a direct #define onto the BB versions (ExposesCheckBB/
IsAttackedBB/etc. are simply "the" implementation now).
- testsee.c/testgenerate.c: deleted the mailbox-vs-BB comparison/speed
harnesses that only existed to validate code just removed
(TestIsAttackedBB, TestGenerateKnightSpeed/KingSpeed/RookSpeed/
BishopSpeed/QueenSpeed, TestFindUnblockedSquaresSpeed's mailbox arm).
TestGetAttacks and the _GenerateAllMoves-vs-_GenerateAllMovesBB
dispatch-overhead benchmark are unaffected -- GetAttacks' mailbox/asm
predecessors were already retired independently, and that pair
compares two still-live BB-only dispatch strategies, not mailbox vs
BB.
Verified: clean release build and a clean DEBUG=1 TEST=1 self-test run
(move generation, perft-style legal-move checks, SAN/ICS parsing,
search smoke test) with no assertion failures.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/testgenerate.c')
| -rw-r--r-- | src/testgenerate.c | 406 |
1 files changed, 6 insertions, 400 deletions
diff --git a/src/testgenerate.c b/src/testgenerate.c index 83c187d..30f26e9 100644 --- a/src/testgenerate.c +++ b/src/testgenerate.c @@ -245,384 +245,10 @@ TestLegalMoveGenerator(void) SystemFreeMemory(ctx); } -// -// board_representation/MOVEGEN_MIGRATION.md section 5's isolated -// cycles/call microbenchmark for the knight generator -- modeled -// directly on testsee.c's TestGetAttacks speed block (interleaved -// call-by-call across opening/middlegame/endgame positions, to cancel -// shared-box noise). Correctness for this piece type is already -// covered by TestMoveGenerator's perft counts (with -// GENERATE_KNIGHT_BITBOARD defined) -- this only answers "is it -// faster," the section 5 gate before a piece type's toggle is -// considered for default-on. -// -void -TestGenerateKnightSpeed(void) -{ - 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/3KN3/8/8/8/8 w - - 0 1", - }; - static const char *rgszLabel[3] = - { - "opening ", "middlegame", "endgame ", - }; - static const COOR rgcKnight[3] = { B1, C3, E5 }; - POSITION posBench; - SEARCHER_THREAD_CONTEXT *ctx; - UINT64 u64MailboxTotal, u64BBTotal, u64Start; - ULONG uIter, u; - ULONG uPly; - const ULONG uCallsPerPosition = 200000; - - Trace("Benchmarking knight move generation: mailbox vs " - "_GenerateKnightBB (interleaved, %lu calls/position)...\n", - uCallsPerPosition); - - ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); - ASSERT(ctx); - uPly = ctx->uPly; - - for (u = 0; u < 3; u++) - { - FenToPosition(&posBench, (char *)rgszFen[u]); - InitializeSearcherContext(&posBench, ctx); - ASSERT(IS_KNIGHT(posBench.rgSquare[rgcKnight[u]].pPiece)); - ASSERT(GET_COLOR(posBench.rgSquare[rgcKnight[u]].pPiece) == - posBench.uToMove); - - // _GenerateKnightBB relies on this being set by its caller - // (normally _GenerateAllMoves, once per node) -- see - // MOVE_STACK's bbFriendlyOccupied field comment in chess.h. - // Computed once per benchmark position, not per call, matching - // how the real call site amortizes it. - ctx->sMoveStack.bbFriendlyOccupied = - _BuildFriendlySideBB(&posBench, posBench.uToMove); - - u64MailboxTotal = 0; - u64BBTotal = 0; - for (uIter = 0; uIter < uCallsPerPosition; uIter++) - { - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - GenerateWhiteKnight(&ctx->sMoveStack, &posBench, rgcKnight[u]); - u64MailboxTotal += (SystemReadTimeStampCounter() - u64Start); - - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - _GenerateKnightBB(&ctx->sMoveStack, &posBench, rgcKnight[u]); - u64BBTotal += (SystemReadTimeStampCounter() - u64Start); - } - printf(" %s: mailbox %" COMPILER_LONGLONG_UNSIGNED_FORMAT - " cycles/call, _GenerateKnightBB %" - COMPILER_LONGLONG_UNSIGNED_FORMAT " cycles/call " - "(BB is %.2fx mailbox)\n", - rgszLabel[u], - u64MailboxTotal / uCallsPerPosition, - u64BBTotal / uCallsPerPosition, - (double)u64BBTotal / (double)u64MailboxTotal); - } - SystemFreeMemory(ctx); -} - -// -// Same shape as TestGenerateKnightSpeed, for the king. Opening position -// has castling rights but blocked by intervening pieces (exercises the -// emptiness-check branch's cost without ever actually reaching -// _AddCastle); middlegame is already castled (no rights, cheapest -// castling-tail exit); endgame is a bare king on an open board (no -// rights either, but the most normal-move destinations to enumerate). -// -void -TestGenerateKingSpeed(void) -{ - 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/4K3/8/8/8/8 w - - 0 1", - }; - static const char *rgszLabel[3] = - { - "opening ", "middlegame", "endgame ", - }; - static const COOR rgcKing[3] = { E1, G1, E5 }; - POSITION posBench; - SEARCHER_THREAD_CONTEXT *ctx; - UINT64 u64MailboxTotal, u64BBTotal, u64Start; - ULONG uIter, u; - ULONG uPly; - const ULONG uCallsPerPosition = 200000; - - Trace("Benchmarking king move generation: mailbox vs " - "_GenerateKingBB (interleaved, %lu calls/position)...\n", - uCallsPerPosition); - - ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); - ASSERT(ctx); - uPly = ctx->uPly; - - for (u = 0; u < 3; u++) - { - FenToPosition(&posBench, (char *)rgszFen[u]); - InitializeSearcherContext(&posBench, ctx); - ASSERT(IS_KING(posBench.rgSquare[rgcKing[u]].pPiece)); - ASSERT(GET_COLOR(posBench.rgSquare[rgcKing[u]].pPiece) == - posBench.uToMove); - - ctx->sMoveStack.bbFriendlyOccupied = - _BuildFriendlySideBB(&posBench, posBench.uToMove); - - u64MailboxTotal = 0; - u64BBTotal = 0; - for (uIter = 0; uIter < uCallsPerPosition; uIter++) - { - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - GenerateWhiteKing(&ctx->sMoveStack, &posBench, rgcKing[u]); - u64MailboxTotal += (SystemReadTimeStampCounter() - u64Start); - - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - _GenerateKingBB(&ctx->sMoveStack, &posBench, rgcKing[u]); - u64BBTotal += (SystemReadTimeStampCounter() - u64Start); - } - printf(" %s: mailbox %" COMPILER_LONGLONG_UNSIGNED_FORMAT - " cycles/call, _GenerateKingBB %" - COMPILER_LONGLONG_UNSIGNED_FORMAT " cycles/call " - "(BB is %.2fx mailbox)\n", - rgszLabel[u], - u64MailboxTotal / uCallsPerPosition, - u64BBTotal / uCallsPerPosition, - (double)u64BBTotal / (double)u64MailboxTotal); - } - SystemFreeMemory(ctx); -} - -// -// Same shape as TestGenerateKnightSpeed/TestGenerateKingSpeed, for the -// rook -- but unlike those two, this piece type is expected to -// actually win: rook a1 is fully blocked in the opening (0 -// destinations, cheapest case either way), partially open in the -// middlegame, and nearly fully open in the endgame (up to 13 -// destinations along an empty file/rank) -- exactly the case a -// 4-direction ray walk pays for and a magic lookup doesn't. -// -void -TestGenerateRookSpeed(void) -{ - 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/R7 w - - 0 1", - }; - static const char *rgszLabel[3] = - { - "opening ", "middlegame", "endgame ", - }; - static const COOR rgcRook[3] = { A1, A1, A1 }; - POSITION posBench; - SEARCHER_THREAD_CONTEXT *ctx; - UINT64 u64MailboxTotal, u64BBTotal, u64Start; - ULONG uIter, u; - ULONG uPly; - const ULONG uCallsPerPosition = 200000; - - Trace("Benchmarking rook move generation: mailbox vs " - "_GenerateRookBB (interleaved, %lu calls/position)...\n", - uCallsPerPosition); - - ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); - ASSERT(ctx); - uPly = ctx->uPly; - - for (u = 0; u < 3; u++) - { - FenToPosition(&posBench, (char *)rgszFen[u]); - InitializeSearcherContext(&posBench, ctx); - ASSERT(IS_ROOK(posBench.rgSquare[rgcRook[u]].pPiece)); - ASSERT(GET_COLOR(posBench.rgSquare[rgcRook[u]].pPiece) == - posBench.uToMove); - - ctx->sMoveStack.bbFriendlyOccupied = - _BuildFriendlySideBB(&posBench, posBench.uToMove); - ctx->sMoveStack.bbOccupied = _BuildFullOccupiedBB(&posBench); - - u64MailboxTotal = 0; - u64BBTotal = 0; - for (uIter = 0; uIter < uCallsPerPosition; uIter++) - { - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - GenerateRook(&ctx->sMoveStack, &posBench, rgcRook[u]); - u64MailboxTotal += (SystemReadTimeStampCounter() - u64Start); - - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - _GenerateRookBB(&ctx->sMoveStack, &posBench, rgcRook[u]); - u64BBTotal += (SystemReadTimeStampCounter() - u64Start); - } - printf(" %s: mailbox %" COMPILER_LONGLONG_UNSIGNED_FORMAT - " cycles/call, _GenerateRookBB %" - COMPILER_LONGLONG_UNSIGNED_FORMAT " cycles/call " - "(BB is %.2fx mailbox)\n", - rgszLabel[u], - u64MailboxTotal / uCallsPerPosition, - u64BBTotal / uCallsPerPosition, - (double)u64BBTotal / (double)u64MailboxTotal); - } - SystemFreeMemory(ctx); -} - -// -// Same shape as TestGenerateRookSpeed, for the bishop -- expect the -// same parity result (see MOVEGEN_MIGRATION.md section 3's rook -// entry for why), not a different outcome, since the underlying -// reason applies identically to diagonals. -// -void -TestGenerateBishopSpeed(void) -{ - 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/B7 w - - 0 1", - }; - static const char *rgszLabel[3] = - { - "opening ", "middlegame", "endgame ", - }; - static const COOR rgcBishop[3] = { C1, D3, A1 }; - POSITION posBench; - SEARCHER_THREAD_CONTEXT *ctx; - UINT64 u64MailboxTotal, u64BBTotal, u64Start; - ULONG uIter, u; - ULONG uPly; - const ULONG uCallsPerPosition = 200000; - - Trace("Benchmarking bishop move generation: mailbox vs " - "_GenerateBishopBB (interleaved, %lu calls/position)...\n", - uCallsPerPosition); - - ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); - ASSERT(ctx); - uPly = ctx->uPly; - - for (u = 0; u < 3; u++) - { - FenToPosition(&posBench, (char *)rgszFen[u]); - InitializeSearcherContext(&posBench, ctx); - ASSERT(IS_BISHOP(posBench.rgSquare[rgcBishop[u]].pPiece)); - ASSERT(GET_COLOR(posBench.rgSquare[rgcBishop[u]].pPiece) == - posBench.uToMove); - - ctx->sMoveStack.bbFriendlyOccupied = - _BuildFriendlySideBB(&posBench, posBench.uToMove); - ctx->sMoveStack.bbOccupied = _BuildFullOccupiedBB(&posBench); - - u64MailboxTotal = 0; - u64BBTotal = 0; - for (uIter = 0; uIter < uCallsPerPosition; uIter++) - { - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - GenerateBishop(&ctx->sMoveStack, &posBench, rgcBishop[u]); - u64MailboxTotal += (SystemReadTimeStampCounter() - u64Start); - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - _GenerateBishopBB(&ctx->sMoveStack, &posBench, rgcBishop[u]); - u64BBTotal += (SystemReadTimeStampCounter() - u64Start); - } - printf(" %s: mailbox %" COMPILER_LONGLONG_UNSIGNED_FORMAT - " cycles/call, _GenerateBishopBB %" - COMPILER_LONGLONG_UNSIGNED_FORMAT " cycles/call " - "(BB is %.2fx mailbox)\n", - rgszLabel[u], - u64MailboxTotal / uCallsPerPosition, - u64BBTotal / uCallsPerPosition, - (double)u64BBTotal / (double)u64MailboxTotal); - } - SystemFreeMemory(ctx); -} - -// -// Same shape as TestGenerateRookSpeed/TestGenerateBishopSpeed, for the -// queen -- expect the same parity-or-slightly-worse result, plus an -// extra fixed cost this time (two magic lookups instead of one) -- -// see MOVEGEN_MIGRATION.md section 3's rook entry for why a win was -// never really on the table for this piece type either. -// -void -TestGenerateQueenSpeed(void) -{ - static const char *rgszFen[3] = - { - "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", - "r1b2rk1/pp2bppp/2n1pn2/2pp4/3P4/1QNBPN2/PP3PPP/R3KB1R w - - 0 1", - "8/5k2/8/3K4/8/8/8/Q7 w - - 0 1", - }; - static const char *rgszLabel[3] = - { - "opening ", "middlegame", "endgame ", - }; - static const COOR rgcQueen[3] = { D1, B3, A1 }; - POSITION posBench; - SEARCHER_THREAD_CONTEXT *ctx; - UINT64 u64MailboxTotal, u64BBTotal, u64Start; - ULONG uIter, u; - ULONG uPly; - const ULONG uCallsPerPosition = 200000; - - Trace("Benchmarking queen move generation: mailbox vs " - "_GenerateQueenBB (interleaved, %lu calls/position)...\n", - uCallsPerPosition); - - ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); - ASSERT(ctx); - uPly = ctx->uPly; - for (u = 0; u < 3; u++) - { - FenToPosition(&posBench, (char *)rgszFen[u]); - InitializeSearcherContext(&posBench, ctx); - ASSERT(IS_QUEEN(posBench.rgSquare[rgcQueen[u]].pPiece)); - ASSERT(GET_COLOR(posBench.rgSquare[rgcQueen[u]].pPiece) == - posBench.uToMove); - ctx->sMoveStack.bbFriendlyOccupied = - _BuildFriendlySideBB(&posBench, posBench.uToMove); - ctx->sMoveStack.bbOccupied = _BuildFullOccupiedBB(&posBench); - - u64MailboxTotal = 0; - u64BBTotal = 0; - for (uIter = 0; uIter < uCallsPerPosition; uIter++) - { - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - GenerateQueen(&ctx->sMoveStack, &posBench, rgcQueen[u]); - u64MailboxTotal += (SystemReadTimeStampCounter() - u64Start); - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - _GenerateQueenBB(&ctx->sMoveStack, &posBench, rgcQueen[u]); - u64BBTotal += (SystemReadTimeStampCounter() - u64Start); - } - printf(" %s: mailbox %" COMPILER_LONGLONG_UNSIGNED_FORMAT - " cycles/call, _GenerateQueenBB %" - COMPILER_LONGLONG_UNSIGNED_FORMAT " cycles/call " - "(BB is %.2fx mailbox)\n", - rgszLabel[u], - u64MailboxTotal / uCallsPerPosition, - u64BBTotal / uCallsPerPosition, - (double)u64BBTotal / (double)u64MailboxTotal); - } - SystemFreeMemory(ctx); -} // // Whole-node dispatch benchmark: _GenerateAllMoves (mailbox @@ -700,21 +326,16 @@ TestGenerateAllMovesSpeed(void) // // board_representation/MOVEGEN_MIGRATION.md's never-started -// _FindUnblockedSquares/WouldGiveCheck candidate. Unlike every other +// _FindUnblockedSquaresBB/WouldGiveCheck candidate. Unlike every other // benchmark in this file, this does NOT measure the generator call in -// isolation: _FindUnblockedSquares writes ~20 square entries that may +// isolation: _FindUnblockedSquaresBB writes ~20 square entries that may // never be read, and WouldGiveCheck reads only 1-3 of them per move, // so a bitboard version that looks like a big win building the table // can give some of it back at query time. The real per-node cost is -// the pair -- one _FindUnblockedSquares(BB) call followed by +// the pair -- one _FindUnblockedSquaresBB call followed by // WouldGiveCheck over every move actually generated at that node -- // so that's what gets timed here, same interleaved-call methodology -// as the rest of this file. WouldGiveCheck's own body is selected at -// compile time by UNBLOCKED_BITBOARD (SQUARE_IS_UNBLOCKED/ -// DIR_FROM_SQ_TO_KING), so unlike TestGenerateAllMovesSpeed this can't -// compare both implementations by name in one binary -- run this test -// in two builds (with and without -DUNBLOCKED_BITBOARD) and compare -// the printed cycles/pair across them. +// as the rest of this file. // void TestFindUnblockedSquaresSpeed(void) @@ -737,15 +358,8 @@ TestFindUnblockedSquaresSpeed(void) MOVE_STACK *pStack; const ULONG uCallsPerPosition = 50000; - Trace("Benchmarking _FindUnblockedSquares%s + WouldGiveCheck over " - "a generated move list (%lu calls/position) -- compare this " - "build's numbers against a build with the opposite " - "-DUNBLOCKED_BITBOARD setting...\n", -#ifdef UNBLOCKED_BITBOARD - "BB", -#else - " (mailbox)", -#endif + Trace("Benchmarking _FindUnblockedSquaresBB + WouldGiveCheck over " + "a generated move list (%lu calls/position)...\n", uCallsPerPosition); ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); @@ -763,11 +377,7 @@ TestFindUnblockedSquaresSpeed(void) // what's under test here, not generation itself. pStack->uEnd[uPly] = pStack->uBegin[uPly]; pStack->sGenFlags[uPly].uAllGenFlags = 0; -#ifdef UNBLOCKED_BITBOARD _FindUnblockedSquaresBB(pStack, &posBench); -#else - _FindUnblockedSquares(pStack, &posBench); -#endif _GenerateAllMoves(pStack, &posBench); uNumMoves = MOVE_COUNT(ctx, uPly); ASSERT(uNumMoves > 0); @@ -794,11 +404,7 @@ TestFindUnblockedSquaresSpeed(void) for (uIter = 0; uIter < uCallsPerPosition; uIter++) { u64Start = SystemReadTimeStampCounter(); -#ifdef UNBLOCKED_BITBOARD _FindUnblockedSquaresBB(pStack, &posBench); -#else - _FindUnblockedSquares(pStack, &posBench); -#endif for (uMove = pStack->uBegin[uPly]; uMove < pStack->uEnd[uPly]; uMove++) |
