/** Copyright (c) Scott Gasch Module Name: testgenerate.c Abstract: Test the move generator. Author: Scott Gasch (scott.gasch@gmail.com) 12 May 2004 Revision History: $Id: testgenerate.c 345 2007-12-02 22:56:42Z scott $ **/ #ifdef TEST #include "chess.h" UINT64 g_uPlyTestLeafNodeCount = 0; UINT64 g_uPlyTestTotalNodeCount = 0; extern CHAR *GenerateRandomLegalFenString(); void PlyTest(SEARCHER_THREAD_CONTEXT *ctx, ULONG uDepth, FLAG fInCheck) { ULONG u; MOVE mv; ULONG uPly = ctx->uPly; FLAG fGivesCheck; #if DEBUG POSITION board; memcpy(&board, &(ctx->sPosition), sizeof(POSITION)); #endif if (uDepth > MAX_PLY_PER_SEARCH) { return; } else if (uDepth == 0) { g_uPlyTestTotalNodeCount++; g_uPlyTestLeafNodeCount++; return; } g_uPlyTestTotalNodeCount++; mv.uMove = 0; GenerateMoves(ctx, mv, (fInCheck ? GENERATE_ESCAPES : GENERATE_ALL_MOVES)); for (u = ctx->sMoveStack.uBegin[uPly]; u < ctx->sMoveStack.uEnd[uPly]; u++) { mv = ctx->sMoveStack.mvf[u].mv; mv.bvFlags |= WouldGiveCheck(ctx, mv); if (MakeMove(ctx, mv)) { ASSERT(!InCheck(&ctx->sPosition, GET_COLOR(mv.pMoved))); fGivesCheck = IS_CHECKING_MOVE(mv); #ifdef DEBUG if (fGivesCheck) { ASSERT(InCheck(&ctx->sPosition, FLIP(GET_COLOR(mv.pMoved)))); } else { ASSERT(!InCheck(&ctx->sPosition, FLIP(GET_COLOR(mv.pMoved)))); } #endif PlyTest(ctx, uDepth - 1, fGivesCheck); UnmakeMove(ctx, mv); ASSERT(PositionsAreEquivalent(&board, &ctx->sPosition)); } } } void TestMoveGenerator(void) { typedef struct _TEST_MOVEGEN { char *szFen; UINT64 uLeaves[7]; } TEST_MOVEGEN; TEST_MOVEGEN x[] = { { "3Q4/1Q4Q1/4Q3/2Q4R/Q4Q2/3Q4/1Q4Rp/1K1BBNNk w - - 0 1", { 218, 0, 0, 0, 0, 0, 0 } }, { "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - - 0 0", { 48, 2039, 97862, 4085603, 193690690ULL, 8031647685ULL } }, { "8/PPP4k/8/8/8/8/4Kppp/8 w - - 0 0", { 18, 290, 5044, 89363, 1745545, 34336777ULL, 749660761ULL } }, { "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 0", { 14, 191, 2812, 43238, 674624, 11030083ULL, 78633661ULL } }, { "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w - - 0 0", { 20, 400, 8902, 197281, 4865609, 119060324ULL, 3195901860ULL } }, }; ULONG u, v; SEARCHER_THREAD_CONTEXT *ctx; POSITION pos; Trace("Testing move generator...\n"); ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); ASSERT(ctx); for (u = 0; u < ARRAY_LENGTH(x); u++) { if (FALSE == FenToPosition(&pos, x[u].szFen)) { UtilPanic(INCONSISTENT_STATE, NULL, NULL, NULL, NULL, __FILE__, __LINE__); } InitializeSearcherContext(&pos, ctx); for (v = 1; v <= 4; v++) { g_uPlyTestLeafNodeCount = 0; g_uPlyTestTotalNodeCount = 0; PlyTest(ctx, v, FALSE); if ((x[u].uLeaves[v-1]) && (g_uPlyTestLeafNodeCount != x[u].uLeaves[v-1])) { UtilPanic(TESTCASE_FAILURE, NULL, "Perft", NULL, NULL, __FILE__, __LINE__); } } } SystemFreeMemory(ctx); } void TestLegalMoveGenerator(void) { ULONG u, v; CHAR *p; POSITION pos; SEARCHER_THREAD_CONTEXT *ctx; MOVE mv; #ifdef DEBUG ULONG uLegalKingMoves; #endif ULONG uTotalLegalMoves; Trace("Testing legal move generator...\n"); ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); ASSERT(ctx); u = 0; do { switch(rand() % 10) { case 0: p = "8/2K1p2k/1Q2B1p1/2N5/q4q2/3p2Pp/7P/8 w - - 2 0"; break; default: p = GenerateRandomLegalFenString(); break; } if (FALSE == FenToPosition(&pos, p)) { UtilPanic(TESTCASE_FAILURE, NULL, "FenToPosition", NULL, NULL, __FILE__, __LINE__); } if (FALSE == IsBoardLegal(&pos)) { continue; } if (TRUE == InCheck(&pos, pos.uToMove)) { #ifdef DEBUG uLegalKingMoves = 0; #endif uTotalLegalMoves = 0; InitializeSearcherContext(&pos, ctx); mv.uMove = 0; GenerateMoves(ctx, mv, GENERATE_ESCAPES); for (v = ctx->sMoveStack.uBegin[0]; v < ctx->sMoveStack.uEnd[0]; v++) { mv = ctx->sMoveStack.mvf[v].mv; ASSERT(mv.bvFlags & MOVE_FLAG_ESCAPING_CHECK); if (TRUE == MakeMove(ctx, mv)) { uTotalLegalMoves++; #ifdef DEBUG if (IS_KING(mv.pMoved)) { uLegalKingMoves++; } #endif UnmakeMove(ctx, mv); } else { ctx->sMoveStack.mvf[v].mv.uMove = 0; } } ASSERT(NUM_KING_MOVES(ctx, ctx->uPly) == uLegalKingMoves); if (uTotalLegalMoves > 1) { ASSERT(!ONE_LEGAL_MOVE(ctx, ctx->uPly)); } else if (uTotalLegalMoves == 1) { ASSERT(ONE_LEGAL_MOVE(ctx, ctx->uPly)); } u++; } } while(u < 1000); 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 // cNonPawns/JumpTable dispatch) vs _GenerateAllMovesBB (direct calls // off pos->bbPieces, no indirect branch) -- see _GenerateAllMovesBB's // block comment in generate.c. Unlike every other benchmark in this // file, this measures the dispatch layer itself, not an individual // piece-type generator in isolation -- the level this migration's // indirect-call-misprediction hypothesis actually predicts a win at. // Must be run in a build with none of the GENERATE_*_BITBOARD toggles // defined, so _GenerateAllMoves keeps its real name (not // macro-substituted to _GenerateAllMovesBB) and both are directly // comparable under their own names in one binary. // void TestGenerateAllMovesSpeed(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", "6k1/8/8/3K4/8/2NBRQ2/8/8 w - - 0 1", }; static const char *rgszLabel[3] = { "opening ", "middlegame", "endgame ", }; POSITION posBench; SEARCHER_THREAD_CONTEXT *ctx; UINT64 u64MailboxTotal, u64BBTotal, u64Start; ULONG uIter, u; ULONG uPly; const ULONG uCallsPerPosition = 200000; Trace("Benchmarking whole-node move generation dispatch: " "_GenerateAllMoves (mailbox JumpTable) vs _GenerateAllMovesBB " "(direct bbPieces dispatch), interleaved, %lu calls/position " "-- only meaningful in a toggle-free build...\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); u64MailboxTotal = 0; u64BBTotal = 0; for (uIter = 0; uIter < uCallsPerPosition; uIter++) { ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; u64Start = SystemReadTimeStampCounter(); _GenerateAllMoves(&ctx->sMoveStack, &posBench); u64MailboxTotal += (SystemReadTimeStampCounter() - u64Start); ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; u64Start = SystemReadTimeStampCounter(); _GenerateAllMovesBB(&ctx->sMoveStack, &posBench); u64BBTotal += (SystemReadTimeStampCounter() - u64Start); } printf(" %s: mailbox %" COMPILER_LONGLONG_UNSIGNED_FORMAT " cycles/call, BB dispatch %" COMPILER_LONGLONG_UNSIGNED_FORMAT " cycles/call " "(BB is %.2fx mailbox)\n", rgszLabel[u], u64MailboxTotal / uCallsPerPosition, u64BBTotal / uCallsPerPosition, (double)u64BBTotal / (double)u64MailboxTotal); } SystemFreeMemory(ctx); } #endif