diff --git a/src/Makefile b/src/Makefile index 66540866..e9edd381 100644 --- a/src/Makefile +++ b/src/Makefile @@ -966,7 +966,7 @@ net: @$(SHELL) ../scripts/net.sh format: - $(CLANG-FORMAT) -i $(SRCS) $(HEADERS) -style=file + $(CLANG-FORMAT) -i $(filter %.cpp,$(SRCS)) $(HEADERS) -style=file default: help diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 86fe0d44..29bd3dd4 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -17,6 +17,7 @@ */ #include "benchmark.h" +#include "numa.h" #include #include @@ -86,6 +87,281 @@ const std::vector Defaults = { }; // clang-format on +// clang-format off +// Human-randomly picked 5 games with <60 moves from kaka's selfplay games +// only moves for one side +const std::vector> BenchmarkPositions = { + { + "2bakab2/9/c3c1n2/p3p1p1p/1npr5/2P2NP2/P3P3P/2CCB4/4A4/1NBAKR3 b - - 1 1", + "2bakab2/9/c3c1n2/p3p1p1p/1nC1r4/2P2NP2/P3P3P/3CB4/4A4/1NBAKR3 b - - 0 2", + "2bakab2/9/c2c2n2/p3p1p1p/1nC1r4/2P2NP2/P3P3P/2NCB4/4A4/2BAKR3 b - - 2 3", + "2baka3/9/c2cb1n2/p1C1p1p1p/1n2r4/2P2NP2/P3P3P/2NCB4/4A4/2BAKR3 b - - 4 4", + "2baka3/9/2ccb1n2/p1C1p1N1p/1n2r4/2P3P2/P3P3P/2NCB4/4A4/2BAKR3 b - - 0 5", + "2baka3/9/2ccb1n2/p1C1p1N1p/1n5r1/1NP3P2/P3P3P/3CB4/4A4/2BAKR3 b - - 2 6", + "2baka3/9/1c1cb1n2/pC2p1N1p/1n5r1/1NP3P2/P3P3P/3CB4/4A4/2BAKR3 b - - 4 7", + "2bak4/4a4/1c1cb1n2/pC2p1N1p/1n5r1/PNP3P2/4P3P/3CB4/4A4/2BAKR3 b - - 6 8", + "2bak4/4a4/1c2b1n2/pC2p1N1p/1n5r1/P1Pc2P2/4P3P/N2CB4/4A4/2BAKR3 b - - 8 9", + "2bak4/4a4/1c2b1n2/pC1cp1N1p/1n5r1/P1P2RP2/4P3P/N2CB4/4A4/2BAK4 b - - 10 10", + "2bak4/4a4/2c1b1n2/pC1cp1N1p/1n5r1/P1P2RP2/4P3P/N2CB4/9/2BAKA3 b - - 12 11", + "2bak4/2c1a4/4b1n2/pC1cp1N1p/1n5r1/P1P2RP2/4P3P/N2CB4/4A4/2B1KA3 b - - 14 12", + "2bak4/2c1a4/4b1n2/pCc1p1N1p/1n5r1/P1P2RP1P/4P4/N2CB4/4A4/2B1KA3 b - - 16 13", + "2bak4/1c2a4/4b1n2/pCc1p1N1p/1n5r1/P1P2RP1P/4P4/N3B4/3CA4/2B1KA3 b - - 18 14", + "2bak4/1c2a4/4b1n2/pCc1p1N1p/1n1r5/P1P2RP1P/4P4/N2AB4/3C5/2B1KA3 b - - 20 15", + "2bak4/1c2a4/4b1n2/pCc1p1N1p/1n7/P1P2RP1P/4P4/N2rB4/6C2/2B1KA3 b - - 1 16", + "2bak4/1c2a4/4N1n2/pCc1p3p/9/P1P2RP1P/n3P4/N2rB4/6C2/2B1KA3 b - - 0 17", + "3ak4/1c2a4/4b1C2/pCc1p3p/9/P1P2RP1P/n3P4/N2rB4/9/2B1KA3 b - - 0 18", + "3ak4/1c2a4/4b1C2/p1c1p3p/9/PCP2RP1P/n3P4/N3B4/3r5/2B1KA3 b - - 2 19", + "3ak4/1c2a4/4b1C2/p1c1p3p/9/PCP2RP1P/4P4/N1n1B4/3rA4/2B1K4 b - - 4 20", + "3ak4/1c2a4/4b1C2/p1c1p3p/9/PCP2RP1P/4P4/N1n1B4/4r4/2B2K3 b - - 1 21", + "3ak4/1c2a4/4b1C2/p1c1p3p/2P6/PC3RP1P/4P4/N1n1B4/3r5/2B2K3 b - - 3 22", + "3ak4/1c2a4/4b1C2/p1c1p3p/2P6/PC3RP1P/4P4/N1n1B4/5K3/2Br5 b - - 5 23", + "3ak4/1c7/4bRC2/p1c1p3p/2P6/PC4P1P/4P4/N1n1B4/5K3/2Br5 b - - 0 24", + "3ak4/1c7/4bRC2/p1c1p3p/2P6/PC4P1P/4P4/N1n1B4/3r5/2B2K3 b - - 2 25", + "4k4/1c2a4/4b1C2/p1c1p3p/2P6/PC3RP1P/4P4/N1n1B4/3r5/2B2K3 b - - 4 26", + "2c1k4/1c2a4/4b1C2/p1P1p3p/9/PC3RP1P/4P4/N1n1B4/3r5/2B2K3 b - - 6 27", + "2c1k4/1c2a4/4b1C2/p1P1p3p/9/P4RP1P/4P4/N3B4/n2r5/1CB2K3 b - - 8 28", + "4k4/1c2a4/4b1C2/p1P1p3p/9/P1R3P1P/4P4/N3B4/n2r5/1Cc2K3 b - - 1 29", + "4k4/1c2a4/4b1C2/p1P1p3p/9/P1R3P1P/4P4/N3B4/n4K3/1Ccr5 b - - 3 30", + "4k4/1c2a4/4b1C2/p1c1p3p/9/P5P1P/4P4/N3B4/n1R2K3/1C1r5 b - - 1 31", + "4k4/1c2a4/4b1C2/p1c1p3p/9/P5P1P/4P4/N3B4/R4K3/1r7 b - - 0 32", + "4k4/1c2a4/4b1C2/p1c1p3p/6P2/P7P/4P4/Nr2B4/R4K3/9 b - - 2 33", + "4k4/1c2a4/4b1C2/p3p3p/2c2P3/P7P/4P4/Nr2B4/R4K3/9 b - - 4 34", + "4k4/1c2a4/4b1C2/p3p3p/2c2P3/P7P/4P4/N3r4/1R3K3/9 b - - 1 35", + "4k4/4a4/4b1C2/p3p3p/2c2P3/P7P/4P4/Nc2r4/5K3/1R7 b - - 3 36", + "4k4/4a4/4b1C2/p3p3p/2c3P2/P7P/4P4/Nc4r2/5K3/1R7 b - - 5 37", + "4k4/4a4/4b1C2/p3p3p/2c2P3/P7P/4P4/Nc6r/5K3/1R7 b - - 7 38", + "4k4/4a4/4b1C2/p3p3p/2c2P3/P7P/4P4/Nc3K3/8r/1R7 b - - 9 39", + "4k4/4a4/4b2C1/p3p3p/2c2P3/P7P/4P4/Nc3K3/6r2/1R7 b - - 11 40", + "4k4/4a4/4b1C2/p3p3p/2c2P3/P7P/4P4/Nc3K3/7r1/1R7 b - - 13 41", + "4k4/4a4/4b1C2/p3p3p/2c2P1r1/P7P/4P4/Nc2K4/9/1R7 b - - 15 42", + "4k4/4a4/4b1C2/p3p3p/2c2r3/P3P3P/9/Nc2K4/9/1R7 b - - 1 43", + "4k4/4a4/4b3C/p3p3p/2c3r2/P3P3P/9/Nc2K4/9/1R7 b - - 3 44", + "4k4/4a4/4b3C/p3p3p/6r2/P3P3P/9/Nc7/2c1K4/1R7 b - - 5 45", + "4k4/4a4/4b3C/p3p3p/6r2/P3P3P/9/Nc7/1c2K4/7R1 b - - 7 46", + "4k4/4a4/4b3C/p3p3p/6r2/P3P3P/9/Nc7/c3K4/R8 b - - 9 47", + "4k4/4a4/4b3C/p7p/4P1r2/P7P/9/Nc7/c3K4/R8 b - - 0 48", + "4k4/4a4/4b3C/p7p/4r4/P7P/9/Nc7/c2K5/R8 b - - 1 49", + "4k4/9/4ba2C/p7p/4r4/PN6P/9/1c7/c2K5/R8 b - - 3 50", + "4k4/9/5a1C1/p7p/4r1b2/PN6P/9/1c7/c2K5/R8 b - - 5 51", + "4k4/9/5a1C1/p7p/6b2/PN6P/9/1c1K5/c3r4/R8 b - - 7 52", + "4k4/9/5a3/p7p/6b2/PN2r3P/7C1/1c1K5/c8/R8 b - - 9 53", + "4k4/9/5a3/p7p/6b2/Pr6P/7C1/1c1K5/c8/4R4 b - - 1 54", + "4k4/4a4/9/p7p/6b2/Pr6P/4C4/1c1K5/c8/4R4 b - - 3 55", + "4k4/9/3a5/p7p/6b2/Pr6P/3C5/1c1K5/c8/4R4 b - - 5 56", + "5k3/9/3a5/p7p/6b2/Pr6P/3C5/1c1K5/c3R4/9 b - - 7 57", + }, + { + "r1bakr3/4a4/2ncb1n2/2p1p1p1p/p8/2PN5/P3P1P1P/C3B4/6Cc1/1RBAKA1NR w - - 0 1", + "r1bakr3/4a4/2ncb1n2/2N1p3p/p5p2/2P6/P3P1P1P/C3B4/6Cc1/1RBAKA1NR w - - 1 2", + "r1bakr3/4a4/2ncb1n2/2N1p2cp/p5p2/2P6/P3P1P1P/C3B4/2C6/1RBAKA1NR w - - 3 3", + "r1bakr3/4a4/3cN1n2/4p2cp/p2n2p2/2P6/P3P1P1P/C3B4/2C6/1RBAKA1NR w - - 1 4", + "r2akr3/4a4/3cb1n2/4p2cp/pR1n2p2/2P6/P3P1P1P/C3B4/2C6/2BAKA1NR w - - 0 5", + "1r1akr3/4a4/3cb1n2/4p2cp/p2R2p2/2P6/P3P1P1P/C3B4/2C6/2BAKA1NR w - - 1 6", + "1r1akr3/4a4/3cb1n2/4p3p/p2R2pc1/2P6/P3P1P1P/C3B1N2/2C6/2BAKA2R w - - 3 7", + "1r1ak4/4a4/3cb1n2/4p3p/p5pc1/2PR5/P3P1P1P/C3B1N2/2C2r3/2BAKA2R w - - 5 8", + "1r1ak4/4a4/3cb1n2/4p3p/p4rpc1/2PR5/P3P1P1P/C3B1N2/2C1A4/2BAK3R w - - 7 9", + "1r1ak4/4a4/3cb1n2/4p3p/p1r3pc1/3R5/P3P1P1P/C3B1N2/2C1A4/2BAK3R w - - 0 10", + "1r1ak4/4a4/3cb4/4p3p/p1r2npc1/2CR5/P3P1P1P/C3B1N2/4A4/2BAK3R w - - 2 11", + "1r1ak4/4a4/3cb2c1/4p3p/p1r2np2/2C4R1/P3P1P1P/C3B1N2/4A4/2BAK3R w - - 4 12", + "1r1ak4/4a4/3cb2c1/4p3p/p1r2n3/2C3pR1/P3P3P/C3B1N2/4A4/2BAK3R w - - 0 13", + "1r1aka3/9/3cb2c1/4p3p/p1r2n3/2C3R2/P3P3P/C3B1N2/4A4/2BAK3R w - - 1 14", + "1r1aka3/9/2c1b2c1/4p3p/p1r2n3/2C3R2/P3P3P/C3B1N2/4A4/2BAK2R1 w - - 3 15", + "1r1aka3/9/2c1b2c1/4p3p/p3rn3/2C3R2/P3P3P/2C1B1N2/4A4/2BAK2R1 w - - 5 16", + "1r1aka3/9/2c1b2c1/4p3p/p2r1n3/2C1P1R2/P7P/2C1B1N2/4A4/2BAK2R1 w - - 7 17", + "1r2ka3/4a4/2c1b2c1/4p3p/p2r1n3/2C1P1R2/P7P/3CB1N2/4A4/2BAK2R1 w - - 9 18", + "4ka3/4a4/2c1b2c1/4p3p/pr1r1n1R1/2C1P1R2/P7P/3CB1N2/4A4/2BAK4 w - - 11 19", + "4ka3/4a4/2c1b2c1/4p1R1p/pr3n1R1/2CrP4/P7P/3CB1N2/4A4/2BAK4 w - - 13 20", + "4ka3/4a4/2c1b2c1/4R3p/pr3n1R1/2r1P4/P7P/3CB1N2/4A4/2BAK4 w - - 0 21", + "4ka3/4a4/2c1b2c1/8p/p3rn1R1/2r1P4/P7P/3CB1N2/4A4/2BAK4 w - - 0 22", + "4ka3/4a4/2c1b2c1/8p/p3P2R1/2r6/P5n1P/3CB1N2/4A4/2BAK4 w - - 1 23", + "4ka3/4a4/4b2c1/8p/p3P4/2r6/P5n1P/3CB1N2/4A4/2BAK4 w - - 0 24", + "4ka3/4a4/4b4/8p/p3P4/2B6/P5ncP/3C2N2/4A4/2BAK4 w - - 1 25", + "4ka3/4a4/4b4/8p/p3P4/2B6/c5n1P/C5N2/4A4/2BAK4 w - - 0 26", + "4kab2/4a4/9/8p/C3P4/2B6/c5n1P/6N2/4A4/2BAK4 w - - 1 27", + "4kab2/4a4/9/8p/C3P4/2B6/c3N3P/8n/4A4/2BAK4 w - - 3 28", + "4kab2/4a4/9/8p/C3P4/2B3N2/c7P/9/4A1n2/2BAK4 w - - 5 29", + "3k1ab2/4a4/9/8p/C3P4/2B3N2/c7P/9/4A1n2/2BA1K3 w - - 7 30", + "3k1ab2/4a4/9/8p/3CP4/2B3N2/c6nP/9/4A4/2BA1K3 w - - 9 31", + "3k1ab2/c3a4/9/8p/4P4/2B3N2/3C3nP/9/4A4/2BA1K3 w - - 11 32", + "3k1a3/c3a4/4b4/8p/4P4/2B3N2/6CnP/9/4A4/2BA1K3 w - - 13 33", + "3k1a3/c3a4/9/7Np/4P1b2/2B6/6CnP/9/4A4/2BA1K3 w - - 15 34", + "3k1a3/4a4/9/7Np/4P1b2/2B6/6CnP/4B4/4A4/c2A1K3 w - - 17 35", + "3k1a3/4a4/c8/7Np/4P1b2/2B6/6CnP/4B4/4AK3/3A5 w - - 19 36", + "3k1a3/4a4/5c3/7Np/4P1b2/2B6/6CnP/4BA3/5K3/3A5 w - - 21 37", + "3k1a3/4a4/9/7Np/4P1b2/2B6/5cCnP/4BA3/4K4/3A5 w - - 23 38", + "3k1a3/4a4/9/7N1/4P1b1p/2B6/5cCnP/5A3/4K4/3A2B2 w - - 25 39", + "3k1a3/9/3a5/7N1/4P1b1p/2B6/5cCnP/5A2B/4K4/3A5 w - - 27 40", + "3k1a3/4a4/9/7N1/5Pb1p/2B6/5cCnP/5A2B/4K4/3A5 w - - 29 41", + "3k1a3/4a4/8b/9/5P2p/2B3N2/5cCnP/5A2B/4K4/3A5 w - - 31 42", + "3k1a3/4a4/8b/9/4NP2p/2B6/4c1CnP/5A2B/4K4/3A5 w - - 33 43", + "3k1a3/4a4/8b/2N6/5P2p/2B6/4c1C1P/5A2B/4K1n2/3A5 w - - 35 44", + "4ka3/1N2a4/8b/9/5P2p/2B6/4c1C1P/5A2B/4K1n2/3A5 w - - 37 45", + "4ka3/1N7/5aC1b/9/5P2p/2B6/4c3P/5A2B/4K1n2/3A5 w - - 39 46", + "4k4/1N2a4/5a2b/6C2/5P2p/2B6/4c3P/5A2B/4K1n2/3A5 w - - 41 47", + "4k4/1N2a4/5a2b/6C2/6P1p/2B6/4c3P/5A2n/4K4/3A5 w - - 0 48", + "5k3/1N2a4/5a2b/4C4/6P1p/2B6/4c3P/5A2n/4K4/3A5 w - - 2 49", + "5k3/1N2a4/5a2b/4C4/7Pp/2B6/4c1n1P/5A3/4K4/3A5 w - - 4 50", + "5k3/1N2a4/5a3/4C4/6b1P/2B6/4c1n1P/5A3/4K4/3A5 w - - 1 51", + "5k3/1N2a4/5a3/8C/4c1b1P/2B6/6n1P/5A3/4K4/3A5 w - - 3 52", + }, + { + "rnbakabnr/9/1c7/p3p1p1p/7c1/2P6/P3P1P1P/5C3/C8/RNBAKABNR b - - 0 1", + "rnbakabnr/9/1c7/p3p3p/6pc1/2P6/P3P1P1P/2N2C3/C8/R1BAKABNR b - - 2 2", + "r1bakabnr/9/1cn6/p3p3p/6pc1/2P6/P3P1P1P/2N1BC3/C8/R1BAKA1NR b - - 4 3", + "2bakabnr/9/rcn6/p3p3p/6pc1/2P6/P3P1P1P/2N1BC3/C4N3/R1BAKA2R b - - 6 4", + "2bakab1r/9/rcn3n2/p3p3p/6pc1/2P6/P3P1P1P/2N1BC3/C4N3/R1BAKAR2 b - - 8 5", + "2bakab1r/1c7/r1n3n2/p3p3p/6pc1/2P6/P3P1P1P/2N1BC3/2C2N3/R1BAKAR2 b - - 10 6", + "2bakab1r/6c2/r1n3n2/p3p3p/2P3pc1/9/P3P1P1P/2N1BC3/2C2N3/R1BAKAR2 b - - 12 7", + "2bakab1r/6c2/1rn3n2/p3p3p/2P3pc1/9/P3P1P1P/R1N1BC3/2C2N3/2BAKAR2 b - - 14 8", + "2bakab1r/6c2/1rn6/p3p3p/2P2npc1/6P2/P3P3P/R1N1BC3/2C2N3/2BAKAR2 b - - 16 9", + "2bakab1r/4n1c2/1r7/p3p3p/2P2npc1/6P2/P3P3P/R1N1B1C2/2C2N3/2BAKAR2 b - - 18 10", + "2bakab1r/6c2/1r4n2/p3p3p/2P2nCc1/6P2/P3P3P/R1N1B4/2C2N3/2BAKAR2 b - - 0 11", + "2bakab1r/5c3/1r4n2/p3p1C1p/2P2n1c1/6P2/P3P3P/R1N1B4/2C2N3/2BAKAR2 b - - 2 12", + "2bakab1r/9/1r4n2/p3p1C1p/2P2nPc1/9/P3P3P/R1N1B4/2C2c3/2BAKAR2 b - - 1 13", + "2bakab1r/9/6n2/p3p1C1p/2P2nPc1/9/P3P3P/R1N1B4/1r2Cc3/2BAKAR2 b - - 3 14", + "2bakab1r/9/6n2/p3p1C1p/2P2P3/9/P3P3P/R1N1B2c1/1r2Cc3/2BAKAR2 b - - 0 15", + "2bakabr1/9/6n2/p3p1C1p/2P2P3/4P4/P7P/R1N1B2c1/1r2Cc3/2BAKAR2 b - - 2 16", + "2baka1r1/9/4b1n2/p3p1C1p/2P2P3/P3P4/8P/R1N1B2c1/1r2Cc3/2BAKAR2 b - - 4 17", + "2baka1r1/9/4b1n2/p3p1C1p/2P2P3/P3P4/Rr6P/2N1B2c1/4Cc3/2BAKAR2 b - - 6 18", + "2baka1r1/9/4b1n2/p3p1C1p/2P2P3/P3P4/N7P/4B2c1/4Cc3/2BAKAR2 b - - 0 19", + "2baka1r1/9/4b1n2/p3p3p/2P2P3/P3P4/N7P/4B4/4CcC2/2BAKARc1 b - - 2 20", + "2baka1r1/9/4b1n2/p3p3p/2P1PP3/P8/N4c2P/4B4/4C1C2/2BAKARc1 b - - 4 21", + "2baka3/9/4b1n2/p3p3p/2P1PP3/P1N6/5c1rP/4B4/4C1C2/2BAKARc1 b - - 6 22", + "2baka3/9/4b1n2/p3p3p/2P1PP3/P1N6/7rc/4B4/4C3C/2BAKARc1 b - - 1 23", + "2baka3/9/4b4/p1P1p3p/4PP1n1/P1N6/7rc/4B4/4C3C/2BAKARc1 b - - 3 24", + "2baka3/9/4b4/pNP1p3p/4PP3/P4n3/7rc/4B4/4C3C/2BAKARc1 b - - 5 25", + "2bak4/4a4/4b4/pNP1P3p/5P3/P4n3/7rc/4B4/4C3C/2BAKARc1 b - - 0 26", + "2bak4/4a4/4b4/pNP1P3p/5P3/P8/7rc/4B1n2/5C2C/2BAKARc1 b - - 2 27", + "2bak4/4a4/4b4/pNP1P3p/5P3/P8/5r2c/4B1n2/5CC2/2BAKARc1 b - - 4 28", + "2ba1k3/4a4/4b4/pNP1P3p/5P3/P8/5r2c/4B1n2/5CC2/2BAKA1R1 b - - 0 29", + "2ba1k1R1/4a4/4b4/pNP1P3p/5P3/P8/8c/4B1n2/5rC2/2BAKA3 b - - 1 30", + "2ba5/4ak3/4b4/pNP1P3p/5P3/P8/8c/4B1n2/5rCR1/2BAKA3 b - - 3 31", + "2ba5/4ak1R1/4b4/pNP1P3p/5P3/P8/6c2/4B1n2/5rC2/2BAKA3 b - - 5 32", + "2ba1k1R1/4a4/4b4/pNP1P3p/5P3/P8/6c2/4B1n2/5rC2/2BAKA3 b - - 7 33", + "2ba2R2/4ak3/4b4/pNP1P3p/5P3/P8/6c2/4B1n2/5rC2/2BAKA3 b - - 9 34", + "2ba2R2/4ak3/4b4/pNP1P3p/5r3/P8/6c2/4B1n2/4A1C2/2B1KA3 b - - 1 35", + "2ba2R2/4ak3/4b4/pNP2P2p/5r3/P8/4c4/4B1n2/4A1C2/2B1KA3 b - - 3 36", + "2ba2R2/4ak3/4b4/pNP2r2p/9/P8/4c4/4B1n2/4A1C2/2BK1A3 b - - 1 37", + "2ba5/4ak3/4b4/pNP2r2p/9/P8/4c1R2/4B4/4A1C1n/2BK1A3 b - - 3 38", + "2ba5/4ak3/4b4/p1P5p/9/P1N6/4c1R2/4B4/4ArC1n/2BK1A3 b - - 5 39", + "2ba5/4ak3/2P1b4/p7p/9/P1N6/5cR2/4B4/4ArC1n/2BK1A3 b - - 7 40", + "2ba5/2P1ak3/4bc3/p7p/9/P1N6/6R2/4B4/4ArC1n/2BK1A3 b - - 9 41", + "2ba1k3/2P1a4/4bc3/p7p/4N4/P8/6R2/4B4/4ArC1n/2BK1A3 b - - 11 42", + "2Pa1k3/4a4/4b2c1/p7p/4N4/P8/6R2/4B4/4ArC1n/2BK1A3 b - - 0 43", + "3P1k3/4a4/4b2c1/p8/4N3p/P8/6R2/4B4/4ArC1n/2BK1A3 b - - 0 44", + "3P1k3/4a4/4b4/p8/4N3p/P8/6R2/4B4/4Ar2n/2BK1ACc1 b - - 2 45", + "3P1k3/4a4/4b1R2/p4r3/4N3p/P8/9/4B4/4A3n/2BK1ACc1 b - - 4 46", + "3P1k3/4a4/4b1R2/p2r5/4N3p/P8/9/4B4/4A3n/2B1KACc1 b - - 6 47", + "3P1k3/4a4/4b1R2/p3r1N2/8p/P8/9/4B4/4A3n/2B1KACc1 b - - 8 48", + "3P1k3/4a4/4b1R2/p3r1N2/8p/P8/9/9/4A4/2B1KABc1 b - - 0 49", + }, + { + "rnbaka1nr/9/6c1b/pCp1p1pcp/9/9/P1P1P1P1P/2C6/9/RNBAKABNR w - - 0 1", + "r1baka1nr/9/n5c1b/pCp1p1pcp/9/9/P1P1P1P1P/2C3N2/9/RNBAKAB1R w - - 2 2", + "r1baka1nr/9/n6cb/p1p1p1pcp/9/1C7/P1P1P1P1P/2C3N2/9/RNBAKAB1R w - - 4 3", + "r1baka1n1/8r/n6cb/p1p1p1pcp/9/PC7/2P1P1P1P/2C3N2/9/RNBAKAB1R w - - 6 4", + "r1baka1n1/5r3/n6cb/p1p1p1pcp/9/PC7/2P1P1P1P/N1C3N2/9/R1BAKAB1R w - - 8 5", + "r1b1ka1n1/4ar3/n6cb/p1p1p1pcp/9/PC7/2P1P1P1P/N1C3N2/8R/R1BAKAB2 w - - 10 6", + "r3ka1n1/4ar3/n3b2cb/p1p1p1pcp/9/PC7/2P1P1P1P/N1C3N2/R7R/2BAKAB2 w - - 12 7", + "3rka1n1/4ar3/n3b2cb/p1p1p1pcp/9/PC7/2P1P1P1P/N1C3N2/5R2R/2BAKAB2 w - - 14 8", + "3rka1n1/4a4/n3b2cb/p1p1p1pcp/9/PC7/2P1P1P1P/N1C3N2/4Ar2R/2BAK1B2 w - - 0 9", + "4ka1n1/4a4/n3b2cb/p1p1p1pcp/3r5/PC7/2P1P1P1P/N1C3N2/4AR3/2BAK1B2 w - - 1 10", + "4ka1n1/2n1a4/4b2cb/p1p1p1pcp/3r5/P3C4/2P1P1P1P/N1C3N2/4AR3/2BAK1B2 w - - 3 11", + "4ka1n1/2n1a4/4b2cb/p3p1pcp/2pr5/P3CR3/2P1P1P1P/N1C3N2/4A4/2BAK1B2 w - - 5 12", + "4ka1n1/2n1a4/4b2cb/p3p1pcp/2p4r1/PN2CR3/2P1P1P1P/2C3N2/4A4/2BAK1B2 w - - 7 13", + "4ka1n1/2n1a4/4b2cb/p3p1pc1/2p4rp/PN2CR3/2P1P1P1P/2C1B1N2/4A4/2BAK4 w - - 9 14", + "4kabn1/2n1a4/4b2c1/p3p1pc1/2p4rp/PN1C1R3/2P1P1P1P/2C1B1N2/4A4/2BAK4 w - - 11 15", + "4kabn1/2n1a4/4b2c1/p3p1p1c/2p4rp/PN3R3/2P1P1P1P/2CCB1N2/4A4/2BAK4 w - - 13 16", + "4kabn1/2n1a4/4b3c/N3p1p1c/2p4rp/P4R3/2P1P1P1P/2CCB1N2/4A4/2BAK4 w - - 1 17", + "4kabn1/2n1a4/4b3c/N3p1p2/2p4rp/PR7/2P1P1P1c/2CCB1N2/4A4/2BAK4 w - - 0 18", + "4kabn1/2n1a4/4b4/N3p1p2/2p4rp/PR7/2P1P1P1c/2CCB4/4A4/2BAK4 w - - 0 19", + "4kab2/2n1a4/4b3n/N3p1p2/2p4rp/PR4P2/2P1P3c/2CCB4/4A4/2BAK4 w - - 2 20", + "4kab2/2n1a4/4b3n/N3p1p2/2p5p/PR4P2/2P1P3c/3CB4/2C1A4/2BAK2r1 w - - 4 21", + "4kab2/2n1a4/4b3n/N3p1p2/2p5p/PR4P2/2P1P3c/3CB4/2C4r1/2BAKA3 w - - 6 22", + "4kab2/2n1a4/4b3n/N3p1p2/2p5p/PR4P2/2P1P3c/3CB4/4Cr3/2BAKA3 w - - 8 23", + "4kab2/2n1a4/4b3n/N3p1p2/2p5p/PR4P2/2P1P3c/3CB4/1C1r5/2BAKA3 w - - 10 24", + "4kab2/2n1a4/4b3n/N3p1p2/2p5p/PR4P2/2c1P4/3CB4/1C1rA4/2BAK4 w - - 0 25", + "3akab2/1Rn6/4b3n/N3p1p2/2p5p/P5P2/2c1P4/3CB4/1C1rA4/2BAK4 w - - 2 26", + "3akab2/1Rn6/4b3n/N3p1p2/1Cp5p/P5P2/2c1P4/3CB4/r3A4/2BAK4 w - - 4 27", + "3akab2/1Rn6/4b3n/N3p1p2/2p5C/r5P2/2c1P4/3CB4/4A4/2BAK4 w - - 0 28", + "3akab2/1R7/4b3n/3np1p2/2p5C/rN4P2/2c1P4/3CB4/4A4/2BAK4 w - - 2 29", + "3akab2/1R7/4b3n/3np1p2/2p5C/r5P2/4P4/N1cCB4/4A4/2BAK4 w - - 4 30", + "3akab2/1R7/4b3n/3np1p2/2p5C/r5P2/4P4/3CB4/2c1A4/1NBAK4 w - - 6 31", + "3akab2/9/4b3n/3np1p2/2p5C/r5P2/2c1P4/3CB4/1R2A4/1NBAK4 w - - 8 32", + "3akab2/2n6/4b3n/1R2p1p2/2p5C/r5P2/2c1P4/3CB4/4A4/1NBAK4 w - - 10 33", + "4kab2/2n1a4/4b3n/4R1p2/2p5C/r5P2/2c1P4/3CB4/4A4/1NBAK4 w - - 1 34", + "4kab2/2n1a4/4b3n/4R1p2/4C4/r1p3P2/2c1P4/3CB4/4A4/1NBAK4 w - - 3 35", + "4kab2/2n1a4/4b3n/4R1p2/4C4/r1p3P2/4P4/N1cCB4/4A4/2BAK4 w - - 5 36", + "4kab2/2n1a4/4b3n/5Rp2/4C4/2p3P2/r3P4/N1cCB4/4A4/2BAK4 w - - 7 37", + "3k1ab2/2n1a4/4b3n/5Rp2/4C4/2p3P2/r3P4/N1cCB4/4A4/2BA1K3 w - - 9 38", + "3k1ab2/2n1a4/4b3n/5Rp2/4C4/2B3P2/4r4/N1cC5/4A4/2BA1K3 w - - 0 39", + "3k1ab2/4a4/4b3n/1n4p2/4CR3/2B3P2/4r4/N1cC5/4A4/2BA1K3 w - - 2 40", + "3k1ab2/4a4/4b3n/1n4p2/4CR3/2B3P2/2c1r4/N8/3CA4/2BA1K3 w - - 4 41", + }, + { + "1n1akab1r/9/4bc3/p1p1p3p/5n3/2P3R2/P3P3P/2N2CN1C/4A4/2c1KAB2 b - - 1 1", + "1n1akab2/9/4bc2r/p1p1p3p/5n3/2P3R2/P3P3P/2N1BCN1C/4A4/2c1KA3 b - - 3 2", + "1n1akab2/9/4bc2r/p1p1p3p/5n3/2P2NR2/P3P3P/2N1BC2C/4A4/c3KA3 b - - 5 3", + "1n1akab2/9/4bc2r/p1p1p1Rnp/9/2P2N3/P3P3P/2N1BC2C/4A4/c3KA3 b - - 7 4", + "1n1akab2/9/4b3r/p1p1p1Rnp/9/2P2N3/P3P3P/2N1BA2C/9/c3KA3 b - - 0 5", + "1n1akab2/9/4br3/p1p1p1Rnp/9/2P6/P3P3P/2N1BAN1C/9/c3KA3 b - - 2 6", + "1n1akab2/8n/4br3/p1p1p1R1p/9/2PN5/P3P3P/4BAN1C/9/c3KA3 b - - 4 7", + "1n2kab2/4a3n/4br3/p1p1p1R1p/9/2PN4P/P3P4/4BAN1C/9/c3KA3 b - - 6 8", + "1n2kab2/4a3n/4br3/p1p1R3p/9/2PN4P/P3P4/4BAN1C/9/1c2KA3 b - - 0 9", + "4kab2/3na3n/4br3/p1pR4p/9/2PN4P/P3P4/4BAN1C/9/1c2KA3 b - - 2 10", + "4kab2/4a3n/1n2br3/p1R5p/9/2PN4P/P3P4/4BAN1C/9/1c2KA3 b - - 0 11", + "4kab2/4a3n/1n2br3/p1R5p/9/1cP5P/P3P4/2N1BAN1C/9/4KA3 b - - 2 12", + "4kab2/4a3n/1n2br3/p1R5p/9/2P5P/P3P4/1c2BAN1C/4N4/4KA3 b - - 4 13", + "4kab2/4a3n/1n2br3/p1R5C/9/2P5P/P3P4/4BcN2/4N4/4KA3 b - - 0 14", + "4kab2/4a3n/1n2b4/p1R5C/9/2P5P/P3Pr3/4BcN2/9/4KAN2 b - - 2 15", + "4kab2/4a3n/1n2b4/p5R1C/9/2P5P/P3Pr3/4B1N2/5c3/4KAN2 b - - 4 16", + "4kab2/4a3n/1n2b4/p5R1C/9/2P4NP/P3Pr3/4B4/6c2/4KAN2 b - - 6 17", + "4kab2/4a3n/1n2b4/p7C/9/2P2r1NP/P3P4/4B4/6R2/4KAN2 b - - 0 18", + "4kab2/4a1R1n/1n2b4/p7C/9/2P4rP/P3P4/4B4/9/4KAN2 b - - 1 19", + "4kab2/4a3R/1n2b4/p7C/9/2P5r/P3P4/4B4/9/4KAN2 b - - 0 20", + "4kab2/4a3R/1n2b4/p7C/9/2P6/P3P4/4B3r/4A4/4K1N2 b - - 2 21", + "4kab2/4a3R/1n2b4/p8/9/2P6/P3P4/4r3C/4A4/4K1N2 b - - 1 22", + "4kab2/4a3R/4b4/p8/n8/2P6/P3P4/4rN2C/4A4/4K4 b - - 3 23", + "4kab2/4a4/4b4/p7R/n8/2P6/P3r4/5N2C/4A4/4K4 b - - 1 24", + "4kab2/4a4/4b4/R8/9/2n6/P3r4/5N2C/4A4/4K4 b - - 0 25", + "4kab2/4a4/4b4/R8/9/2n6/P7r/5NC2/4A4/4K4 b - - 2 26", + "4kab2/4a4/4b4/9/9/2n6/R8/5NC2/4A4/4K4 b - - 0 27", + "4kab2/4a4/4b4/9/9/6N2/n8/6C2/4A4/4K4 b - - 1 28", + "4kab2/4a4/4b4/9/9/2n3N2/9/7C1/4A4/4K4 b - - 3 29", + "3k1ab2/4a4/4b4/5N3/9/2n6/9/7C1/4A4/4K4 b - - 5 30", + "3k1ab2/4a4/4b4/5N3/4n4/9/9/3C5/4A4/4K4 b - - 7 31", + "4kab2/4a1N2/4b4/9/4n4/9/9/3C5/4A4/4K4 b - - 9 32", + "3k1ab2/4a1N2/4b4/9/4n4/9/9/9/3CA4/4K4 b - - 11 33", + "3k1ab2/4a4/4b4/5N3/9/2n6/9/9/3CA4/4K4 b - - 13 34", + "3k1ab2/4a4/3Nb4/9/4n4/9/9/9/3CA4/4K4 b - - 15 35", + "3k1ab2/4a4/4b4/1N7/9/9/3n5/9/3CA4/4K4 b - - 17 36", + "3k1ab2/4a4/4b4/1N7/4n4/9/9/9/3CA4/3K5 b - - 19 37", + "3k1ab2/4a4/9/1N1C5/2b1n4/9/9/9/4A4/3K5 b - - 21 38", + "5ab2/3ka4/9/3C5/2b1n4/N8/9/9/4A4/3K5 b - - 23 39", + "5ab2/3ka4/9/3C5/2b6/N8/3n5/9/4A4/4K4 b - - 25 40", + "5a3/3ka4/4b4/9/2b6/N2C5/3n5/9/4A4/4K4 b - - 27 41", + "5a3/3k5/4ba3/1N7/2b6/3C5/3n5/9/4A4/4K4 b - - 29 42", + "5a3/2N1k4/4ba3/9/2b6/3C5/3n5/9/4A4/4K4 b - - 31 43", + "5a3/2N1k4/4ba3/9/2b6/3C5/9/9/2n1A4/5K3 b - - 33 44", + "5a3/2N1k4/4ba3/9/2b6/C8/3n5/9/4A4/5K3 b - - 35 45", + "5a3/3k5/4ba3/3N5/2b6/C8/3n5/9/4A4/5K3 b - - 37 46", + "3k1a3/9/4ba3/3N5/2b6/3C5/3n5/9/4A4/5K3 b - - 39 47", + "4ka3/9/4bN3/9/2b6/3C5/3n5/9/4A4/5K3 b - - 0 48", + "5a3/4k4/4bN3/9/2b6/3C5/3n5/9/4A4/4K4 b - - 2 49", + "5a3/4k4/4b4/7N1/2b6/3C1n3/9/9/4A4/4K4 b - - 4 50", + "5a3/4k4/4b4/7N1/2b6/4C4/9/4n4/4A4/4K4 b - - 6 51", + "5a3/5k3/4b4/9/2b6/4C1N2/9/4n4/4A4/4K4 b - - 8 52", + "9/4ak3/4b4/9/2b6/4C1N2/9/4n4/4A4/3K5 b - - 10 53", + "3a5/5k3/4b4/5N3/2b6/4C4/9/4n4/4A4/3K5 b - - 12 54", + "3a1k3/9/4b4/5N3/2b6/9/4C4/4n4/4A4/3K5 b - - 14 55", + "3a1k3/9/4b4/5N3/2b6/9/5Cn2/9/4A4/3K5 b - - 16 56", + "3ak4/9/4b4/5N3/2b6/9/6n2/9/4A4/3K1C3 b - - 18 57", + "3ak4/6N2/4b4/9/2b6/9/9/4n4/4A4/3K1C3 b - - 20 58", + "3a5/4k1N2/4b4/9/2b6/9/9/4n4/4A4/3KC4 b - - 22 59", + } +}; +// clang-format on + } // namespace namespace Stockfish::Benchmark { @@ -155,4 +431,76 @@ std::vector setup_bench(const std::string& currentFen, std::istream return list; } +BenchmarkSetup setup_benchmark(std::istream& is) { + // TT_SIZE_PER_THREAD is chosen such that roughly half of the hash is used all positions + // for the current sequence have been searched. + static constexpr int TT_SIZE_PER_THREAD = 128; + + static constexpr int DEFAULT_DURATION_S = 150; + + BenchmarkSetup setup{}; + + // Assign default values to missing arguments + int desiredTimeS; + + if (!(is >> setup.threads)) + setup.threads = get_hardware_concurrency(); + else + setup.originalInvocation += std::to_string(setup.threads); + + if (!(is >> setup.ttSize)) + setup.ttSize = TT_SIZE_PER_THREAD * setup.threads; + else + setup.originalInvocation += " " + std::to_string(setup.ttSize); + + if (!(is >> desiredTimeS)) + desiredTimeS = DEFAULT_DURATION_S; + else + setup.originalInvocation += " " + std::to_string(desiredTimeS); + + setup.filledInvocation += std::to_string(setup.threads) + " " + std::to_string(setup.ttSize) + + " " + std::to_string(desiredTimeS); + + auto getCorrectedTime = [&](int ply) { + // time per move is fit roughly based on LTC games + // seconds = 50/{ply+15} + // ms = 50000/{ply+15} + // with this fit 10th move gets 2000ms + // adjust for desired 10th move time + return 50000.0 / (static_cast(ply) + 15.0); + }; + + float totalTime = 0; + for (const auto& game : BenchmarkPositions) + { + setup.commands.emplace_back("ucinewgame"); + int ply = 1; + for (int i = 0; i < static_cast(game.size()); ++i) + { + const float correctedTime = getCorrectedTime(ply); + totalTime += correctedTime; + ply += 1; + } + } + + float timeScaleFactor = static_cast(desiredTimeS * 1000) / totalTime; + + for (const auto& game : BenchmarkPositions) + { + setup.commands.emplace_back("ucinewgame"); + int ply = 1; + for (const std::string& fen : game) + { + setup.commands.emplace_back("position fen " + fen); + + const int correctedTime = static_cast(getCorrectedTime(ply) * timeScaleFactor); + setup.commands.emplace_back("go movetime " + std::to_string(correctedTime)); + + ply += 1; + } + } + + return setup; +} + } // namespace Stockfish diff --git a/src/benchmark.h b/src/benchmark.h index b1eba40f..eb3a52d8 100644 --- a/src/benchmark.h +++ b/src/benchmark.h @@ -27,6 +27,16 @@ namespace Stockfish::Benchmark { std::vector setup_bench(const std::string&, std::istream&); +struct BenchmarkSetup { + int ttSize; + int threads; + std::vector commands; + std::string originalInvocation; + std::string filledInvocation; +}; + +BenchmarkSetup setup_benchmark(std::istream&); + } // namespace Stockfish #endif // #ifndef BENCHMARK_H_INCLUDED diff --git a/src/engine.cpp b/src/engine.cpp index 8a621cab..291bc7b7 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -59,12 +59,13 @@ Engine::Engine(std::optional path) : options["NumaPolicy"] << Option("auto", [this](const Option& o) { set_numa_config_from_option(o); - return numa_config_information_as_string() + "\n" + thread_binding_information_as_string(); + return numa_config_information_as_string() + "\n" + + thread_allocation_information_as_string(); }); options["Threads"] << Option(1, 1, 1024, [this](const Option&) { resize_threads(); - return thread_binding_information_as_string(); + return thread_allocation_information_as_string(); }); options["Hash"] << Option(16, 1, MaxHashMB, [this](const Option& o) { @@ -128,6 +129,10 @@ void Engine::set_on_bestmove(std::function&& f) { + onVerifyNetworks = std::move(f); +} + void Engine::wait_for_search_finished() { threads.main_thread()->wait_for_search_finished(); } void Engine::set_position(const std::string& fen, const std::vector& moves) { @@ -197,7 +202,7 @@ void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; } // network related -void Engine::verify_network() const { network->verify(options["EvalFile"]); } +void Engine::verify_network() const { network->verify(options["EvalFile"], onVerifyNetworks); } void Engine::load_network(const std::string& file) { network.modify_and_replicate( @@ -235,6 +240,8 @@ std::string Engine::visualize() const { return ss.str(); } +int Engine::get_hashfull(int maxAge) const { return tt.hashfull(maxAge); } + std::vector> Engine::get_bound_thread_count_by_numa_node() const { auto counts = threads.get_bound_thread_count_by_numa_node(); const NumaConfig& cfg = numaContext.get_numa_config(); @@ -260,15 +267,9 @@ std::string Engine::numa_config_information_as_string() const { std::string Engine::thread_binding_information_as_string() const { auto boundThreadsByNode = get_bound_thread_count_by_numa_node(); std::stringstream ss; - - size_t threadsSize = threads.size(); - ss << "Using " << threadsSize << (threadsSize > 1 ? " threads" : " thread"); - if (boundThreadsByNode.empty()) return ss.str(); - ss << " with NUMA node thread binding: "; - bool isFirst = true; for (auto&& [current, total] : boundThreadsByNode) @@ -282,4 +283,20 @@ std::string Engine::thread_binding_information_as_string() const { return ss.str(); } +std::string Engine::thread_allocation_information_as_string() const { + std::stringstream ss; + + size_t threadsSize = threads.size(); + ss << "Using " << threadsSize << (threadsSize > 1 ? " threads" : " thread"); + + auto boundThreadsByNodeStr = thread_binding_information_as_string(); + if (boundThreadsByNodeStr.empty()) + return ss.str(); + + ss << " with NUMA node thread binding: "; + ss << boundThreadsByNodeStr; + + return ss.str(); +} + } diff --git a/src/engine.h b/src/engine.h index a83164c1..c6996a98 100644 --- a/src/engine.h +++ b/src/engine.h @@ -79,6 +79,7 @@ class Engine { void set_on_update_full(std::function&&); void set_on_iter(std::function&&); void set_on_bestmove(std::function&&); + void set_on_verify_networks(std::function&&); // network related @@ -93,12 +94,15 @@ class Engine { const OptionsMap& get_options() const; OptionsMap& get_options(); + int get_hashfull(int maxAge = 0) const; + std::string fen() const; void flip(); std::string visualize() const; std::vector> get_bound_thread_count_by_numa_node() const; std::string get_numa_config_as_string() const; std::string numa_config_information_as_string() const; + std::string thread_allocation_information_as_string() const; std::string thread_binding_information_as_string() const; private: @@ -115,7 +119,8 @@ class Engine { TranspositionTable tt; LazyNumaReplicated network; - Search::SearchManager::UpdateContext updateContext; + Search::SearchManager::UpdateContext updateContext; + std::function onVerifyNetworks; }; } // namespace Stockfish diff --git a/src/memory.cpp b/src/memory.cpp index ae303c53..47c901b4 100644 --- a/src/memory.cpp +++ b/src/memory.cpp @@ -212,6 +212,37 @@ void* aligned_large_pages_alloc(size_t allocSize) { #endif +bool has_large_pages() { + +#if defined(_WIN32) + + constexpr size_t page_size = 2 * 1024 * 1024; // 2MB page size assumed + void* mem = aligned_large_pages_alloc_windows(page_size); + if (mem == nullptr) + { + return false; + } + else + { + aligned_large_pages_free(mem); + return true; + } + +#elif defined(__linux__) + + #if defined(MADV_HUGEPAGE) + return true; + #else + return false; + #endif + +#else + + return false; + +#endif +} + // aligned_large_pages_free() will free the previously memory allocated // by aligned_large_pages_alloc(). The effect is a nop if mem == nullptr. diff --git a/src/memory.h b/src/memory.h index 3155a5aa..eaf0261a 100644 --- a/src/memory.h +++ b/src/memory.h @@ -38,6 +38,8 @@ void std_aligned_free(void* ptr); void* aligned_large_pages_alloc(size_t size); void aligned_large_pages_free(void* mem); +bool has_large_pages(); + // Frees memory which was placed there with placement new. // Works for both single objects and arrays of unknown bound. template diff --git a/src/misc.cpp b/src/misc.cpp index 14c154fa..c76bf930 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -123,7 +123,7 @@ class Logger { // // For releases (non-dev builds) we only include the version number: // Pikafish version -std::string engine_info(bool to_uci) { +std::string engine_version_info() { std::stringstream ss; ss << "Pikafish " << version << std::setfill('0'); @@ -152,11 +152,14 @@ std::string engine_info(bool to_uci) { #endif } - ss << (to_uci ? "\nid author " : " by ") << "the Pikafish developers (see AUTHORS file)"; - return ss.str(); } +std::string engine_info(bool to_uci) { + return engine_version_info() + (to_uci ? "\nid author " : " by ") + + "the Pikafish developers (see AUTHORS file)"; +} + // Returns a string trying to describe the compiler we use std::string compiler_info() { @@ -454,7 +457,7 @@ void remove_whitespace(std::string& s) { s.erase(std::remove_if(s.begin(), s.end(), [](char c) { return std::isspace(c); }), s.end()); } -bool is_whitespace(const std::string& s) { +bool is_whitespace(std::string_view s) { return std::all_of(s.begin(), s.end(), [](char c) { return std::isspace(c); }); } diff --git a/src/misc.h b/src/misc.h index ec23bcd8..37f5f81d 100644 --- a/src/misc.h +++ b/src/misc.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #define stringify2(x) #x @@ -35,6 +36,7 @@ namespace Stockfish { +std::string engine_version_info(); std::string engine_info(bool to_uci = false); std::string compiler_info(); @@ -81,8 +83,8 @@ inline TimePoint now() { .count(); } -inline std::vector split(const std::string& s, const std::string& delimiter) { - std::vector res; +inline std::vector split(std::string_view s, std::string_view delimiter) { + std::vector res; if (s.empty()) return res; @@ -104,7 +106,7 @@ inline std::vector split(const std::string& s, const std::string& d } void remove_whitespace(std::string& s); -bool is_whitespace(const std::string& s); +bool is_whitespace(std::string_view s); enum SyncCout { IO_LOCK, diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index fb055380..c62d4b5e 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -161,36 +161,45 @@ NetworkOutput Network::evaluate(const Position& pos, AccumulatorCaches::Cache* c } -void Network::verify(std::string evalfilePath) const { +void Network::verify(std::string evalfilePath, + const std::function& f) const { if (evalfilePath.empty()) evalfilePath = evalFile.defaultName; if (evalFile.current != evalfilePath) { - std::string msg1 = - "Network evaluation parameters compatible with the engine must be available."; - std::string msg2 = "The network file " + evalfilePath + " was not loaded successfully."; - std::string msg3 = "The UCI option EvalFile might need to specify the full path, " - "including the directory name, to the network file."; - std::string msg4 = - "The default net can be downloaded from: " - "https://github.com/official-pikafish/Networks/releases/download/master-net/" - + evalFile.defaultName; - std::string msg5 = "The engine will be terminated now."; - - sync_cout << "info string ERROR: " << msg1 << sync_endl; - sync_cout << "info string ERROR: " << msg2 << sync_endl; - sync_cout << "info string ERROR: " << msg3 << sync_endl; - sync_cout << "info string ERROR: " << msg4 << sync_endl; - sync_cout << "info string ERROR: " << msg5 << sync_endl; + if (f) + { + std::string msg1 = + "Network evaluation parameters compatible with the engine must be available."; + std::string msg2 = "The network file " + evalfilePath + " was not loaded successfully."; + std::string msg3 = "The UCI option EvalFile might need to specify the full path, " + "including the directory name, to the network file."; + std::string msg4 = + "The default net can be downloaded from: " + "https://github.com/official-pikafish/Networks/releases/download/master-net/" + + evalFile.defaultName; + std::string msg5 = "The engine will be terminated now."; + + std::string msg = "ERROR: " + msg1 + '\n' + "ERROR: " + msg2 + '\n' + "ERROR: " + msg3 + + '\n' + "ERROR: " + msg4 + '\n' + "ERROR: " + msg5 + '\n'; + + f(msg); + } + exit(EXIT_FAILURE); } - size_t size = sizeof(*featureTransformer) + sizeof(NetworkArchitecture) * LayerStacks; - sync_cout << "info string NNUE evaluation using " << evalfilePath << " (" - << size / (1024 * 1024) << "MiB, (" << featureTransformer->InputDimensions << ", " - << TransformedFeatureDimensions << ", " << NetworkArchitecture::FC_0_OUTPUTS << ", " - << NetworkArchitecture::FC_1_OUTPUTS << ", 1))" << sync_endl; + if (f) + { + size_t size = sizeof(*featureTransformer) + sizeof(NetworkArchitecture) * LayerStacks; + f("info string NNUE evaluation using " + evalfilePath + " (" + + std::to_string(size / (1024 * 1024)) + "MiB, (" + + std::to_string(featureTransformer->InputDimensions) + ", " + + std::to_string(TransformedFeatureDimensions) + ", " + + std::to_string(NetworkArchitecture::FC_0_OUTPUTS) + ", " + + std::to_string(NetworkArchitecture::FC_1_OUTPUTS) + ", 1))"); + } } diff --git a/src/nnue/network.h b/src/nnue/network.h index 892d2686..6e960de6 100644 --- a/src/nnue/network.h +++ b/src/nnue/network.h @@ -17,10 +17,12 @@ #define NETWORK_H_INCLUDED #include +#include #include #include #include #include +#include #include #include "../memory.h" @@ -56,7 +58,7 @@ class Network { void hint_common_access(const Position& pos, AccumulatorCaches::Cache* cache) const; - void verify(std::string evalfilePath) const; + void verify(std::string evalfilePath, const std::function&) const; NnueEvalTrace trace_evaluate(const Position& pos, AccumulatorCaches::Cache* cache) const; private: diff --git a/src/numa.h b/src/numa.h index db835922..1063721e 100644 --- a/src/numa.h +++ b/src/numa.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -653,7 +654,7 @@ class NumaConfig { NumaIndex n = 0; for (auto&& nodeStr : split(s, ":")) { - auto indices = indices_from_shortened_string(nodeStr); + auto indices = indices_from_shortened_string(std::string(nodeStr)); if (!indices.empty()) { for (auto idx : indices) @@ -1015,7 +1016,7 @@ class NumaConfig { if (s.empty()) return indices; - for (const std::string& ss : split(s, ",")) + for (const auto& ss : split(s, ",")) { if (ss.empty()) continue; @@ -1023,13 +1024,13 @@ class NumaConfig { auto parts = split(ss, "-"); if (parts.size() == 1) { - const CpuIndex c = CpuIndex{str_to_size_t(parts[0])}; + const CpuIndex c = CpuIndex{str_to_size_t(std::string(parts[0]))}; indices.emplace_back(c); } else if (parts.size() == 2) { - const CpuIndex cfirst = CpuIndex{str_to_size_t(parts[0])}; - const CpuIndex clast = CpuIndex{str_to_size_t(parts[1])}; + const CpuIndex cfirst = CpuIndex{str_to_size_t(std::string(parts[0]))}; + const CpuIndex clast = CpuIndex{str_to_size_t(std::string(parts[1]))}; for (size_t c = cfirst; c <= clast; ++c) { indices.emplace_back(c); diff --git a/src/tt.cpp b/src/tt.cpp index 33c44877..624b167a 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -193,13 +193,20 @@ void TranspositionTable::clear(ThreadPool& threads) { // Returns an approximation of the hashtable // occupation during a search. The hash is x permill full, as per UCI protocol. // Only counts entries which match the current generation. -int TranspositionTable::hashfull() const { - +int TranspositionTable::hashfull(int maxAge) const { int cnt = 0; for (int i = 0; i < 1000; ++i) for (int j = 0; j < ClusterSize; ++j) - cnt += table[i].entry[j].is_occupied() - && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8; + { + if (table[i].entry[j].is_occupied()) + { + int age = (generation8 >> GENERATION_BITS) + - ((table[i].entry[j].genBound8 & GENERATION_MASK) >> GENERATION_BITS); + if (age < 0) + age += 1 << (8 - GENERATION_BITS); + cnt += age <= maxAge; + } + } return cnt / ClusterSize; } diff --git a/src/tt.h b/src/tt.h index 1bece002..e7bb5c45 100644 --- a/src/tt.h +++ b/src/tt.h @@ -73,7 +73,7 @@ class TranspositionTable { void resize(size_t mbSize, ThreadPool& threads); // Set TT size void clear(ThreadPool& threads); // Re-initialize memory, multithreaded - int hashfull() + int hashfull(int maxAge = 0) const; // Approximate what fraction of entries (permille) have been written to during this root search void diff --git a/src/uci.cpp b/src/uci.cpp index e9dcdc4a..6c17dc0c 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include "benchmark.h" #include "engine.h" +#include "memory.h" #include "movegen.h" #include "position.h" #include "score.h" @@ -37,6 +39,8 @@ namespace Stockfish { +constexpr auto BenchmarkCommand = "speedtest"; + constexpr auto StartFEN = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w"; template struct overload: Ts... { @@ -46,7 +50,7 @@ struct overload: Ts... { template overload(Ts...) -> overload; -void UCIEngine::print_info_string(const std::string& str) { +void UCIEngine::print_info_string(std::string_view str) { sync_cout_start(); for (auto& line : split(str, "\n")) { @@ -67,11 +71,16 @@ UCIEngine::UCIEngine(int argc, char** argv) : print_info_string(*str); }); + init_search_update_listeners(); +} + +void UCIEngine::init_search_update_listeners() { engine.set_on_iter([](const auto& i) { on_iter(i); }); engine.set_on_update_no_moves([](const auto& i) { on_update_no_moves(i); }); engine.set_on_update_full( [this](const auto& i) { on_update_full(i, engine.get_options()["UCI_ShowWDL"]); }); engine.set_on_bestmove([](const auto& bm, const auto& p) { on_bestmove(bm, p); }); + engine.set_on_verify_networks([](const auto& s) { print_info_string(s); }); } void UCIEngine::loop() { @@ -115,7 +124,7 @@ void UCIEngine::loop() { { // send info strings after the go command is sent for old GUIs and python-chess print_info_string(engine.numa_config_information_as_string()); - print_info_string(engine.thread_binding_information_as_string()); + print_info_string(engine.thread_allocation_information_as_string()); go(is); } else if (token == "position") @@ -133,6 +142,8 @@ void UCIEngine::loop() { engine.flip(); else if (token == "bench") bench(is); + else if (token == BenchmarkCommand) + benchmark(is); else if (token == "d") sync_cout << engine.visualize() << sync_endl; else if (token == "eval") @@ -281,6 +292,163 @@ void UCIEngine::bench(std::istream& args) { engine.set_on_update_full([&](const auto& i) { on_update_full(i, options["UCI_ShowWDL"]); }); } +void UCIEngine::benchmark(std::istream& args) { + // Probably not very important for a test this long, but include for completeness and sanity. + static constexpr int NUM_WARMUP_POSITIONS = 3; + + std::string token; + uint64_t nodes = 0, cnt = 1; + uint64_t nodesSearched = 0; + + engine.set_on_update_full([&](const Engine::InfoFull& i) { nodesSearched = i.nodes; }); + + engine.set_on_iter([](const auto&) {}); + engine.set_on_update_no_moves([](const auto&) {}); + engine.set_on_bestmove([](const auto&, const auto&) {}); + engine.set_on_verify_networks([](const auto&) {}); + + Benchmark::BenchmarkSetup setup = Benchmark::setup_benchmark(args); + + const int numGoCommands = count_if(setup.commands.begin(), setup.commands.end(), + [](const std::string& s) { return s.find("go ") == 0; }); + + TimePoint totalTime = 0; + + // Set options once at the start. + auto ss = std::istringstream("name Threads value " + std::to_string(setup.threads)); + setoption(ss); + ss = std::istringstream("name Hash value " + std::to_string(setup.ttSize)); + setoption(ss); + + // Warmup + for (const auto& cmd : setup.commands) + { + std::istringstream is(cmd); + is >> std::skipws >> token; + + if (token == "go") + { + // One new line is produced by the search, so omit it here + std::cerr << "\rWarmup position " << cnt++ << '/' << NUM_WARMUP_POSITIONS; + + Search::LimitsType limits = parse_limits(is); + + TimePoint elapsed = now(); + + // Run with silenced network verification + engine.go(limits); + engine.wait_for_search_finished(); + + totalTime += now() - elapsed; + + nodes += nodesSearched; + nodesSearched = 0; + } + else if (token == "position") + position(is); + else if (token == "ucinewgame") + { + engine.search_clear(); // search_clear may take a while + } + + if (cnt > NUM_WARMUP_POSITIONS) + break; + } + + std::cerr << "\n"; + + cnt = 1; + nodes = 0; + + int numHashfullReadings = 0; + constexpr int hashfullAges[] = {0, 999}; // Only normal hashfull and touched hash. + int totalHashfull[std::size(hashfullAges)] = {0}; + int maxHashfull[std::size(hashfullAges)] = {0}; + + auto updateHashfullReadings = [&]() { + numHashfullReadings += 1; + + for (int i = 0; i < static_cast(std::size(hashfullAges)); ++i) + { + const int hashfull = engine.get_hashfull(hashfullAges[i]); + maxHashfull[i] = std::max(maxHashfull[i], hashfull); + totalHashfull[i] += hashfull; + } + }; + + engine.search_clear(); // search_clear may take a while + + for (const auto& cmd : setup.commands) + { + std::istringstream is(cmd); + is >> std::skipws >> token; + + if (token == "go") + { + // One new line is produced by the search, so omit it here + std::cerr << "\rPosition " << cnt++ << '/' << numGoCommands; + + Search::LimitsType limits = parse_limits(is); + + TimePoint elapsed = now(); + + // Run with silenced network verification + engine.go(limits); + engine.wait_for_search_finished(); + + totalTime += now() - elapsed; + + updateHashfullReadings(); + + nodes += nodesSearched; + nodesSearched = 0; + } + else if (token == "position") + position(is); + else if (token == "ucinewgame") + { + engine.search_clear(); // search_clear may take a while + } + } + + totalTime = std::max(totalTime, 1); // Ensure positivity to avoid a 'divide by zero' + + dbg_print(); + + std::cerr << "\n"; + + static_assert( + std::size(hashfullAges) == 2 && hashfullAges[0] == 0 && hashfullAges[1] == 999, + "Hardcoded for display. Would complicate the code needlessly in the current state."); + + std::string threadBinding = engine.thread_binding_information_as_string(); + if (threadBinding.empty()) + threadBinding = "none"; + + std::cerr << "===========================" + << "\nVersion : " + << engine_version_info() + // "\nCompiled by : " + << compiler_info() + << "Large pages : " << (has_large_pages() ? "yes" : "no") + << "\nUser invocation : " << BenchmarkCommand << " " + << setup.originalInvocation << "\nFilled invocation : " << BenchmarkCommand + << " " << setup.filledInvocation + << "\nAvailable processors : " << engine.get_numa_config_as_string() + << "\nThread count : " << setup.threads + << "\nThread binding : " << threadBinding + << "\nTT size [MiB] : " << setup.ttSize + << "\nHash max, avg [per mille] : " + << "\n single search : " << maxHashfull[0] << ", " + << totalHashfull[0] / numHashfullReadings + << "\n single game : " << maxHashfull[1] << ", " + << totalHashfull[1] / numHashfullReadings + << "\nTotal nodes searched : " << nodes + << "\nTotal search time [s] : " << totalTime / 1000.0 + << "\nNodes/second : " << 1000 * nodes / totalTime << std::endl; + + init_search_update_listeners(); +} void UCIEngine::setoption(std::istringstream& is) { engine.wait_for_search_finished(); diff --git a/src/uci.h b/src/uci.h index 57fe9eef..c648f905 100644 --- a/src/uci.h +++ b/src/uci.h @@ -57,10 +57,11 @@ class UCIEngine { Engine engine; CommandLine cli; - static void print_info_string(const std::string& str); + static void print_info_string(std::string_view str); void go(std::istringstream& is); void bench(std::istream& args); + void benchmark(std::istream& args); void position(std::istringstream& is); void setoption(std::istringstream& is); std::uint64_t perft(const Search::LimitsType&); @@ -69,6 +70,8 @@ class UCIEngine { static void on_update_full(const Engine::InfoFull& info, bool showWDL); static void on_iter(const Engine::InfoIter& info); static void on_bestmove(std::string_view bestmove, std::string_view ponder); + + void init_search_update_listeners(); }; } // namespace Stockfish