今回はベンチマーク部です。
UCIプロトコルの拡張コマンドで、”bench”とタイプすると事前に用意してあった局面から探索を開始して、探索終了するまでの時間を計測し、nps(nodes per second=1秒あたりの探索ノード数)などを表示します。
この機能をつけておかないと、高速化するはずの改良によって探索が速くなったのかどうかが判断つかないため、将棋ソフトの開発の場合でも早い段階で用意しておくべきだと思います。
なお、探索内容が毎回異なるとベンチマークとしての役割をなさないため、基本的にはシングルスレッドで固定深さで実行し、毎回探索ノード数は同じになるようにします。毎回同じでないとしたら、置換表の処理が何らかバグっているだとか、初期化を忘れているメモリがあるだとか、そういうのが原因として考えられます。
私もやねうら王の開発当初、ベンチマークの結果の探索ノード数が毎回わずかに異なるので、ログを書き出して、どこからどこまでは一致するのかを絞り込んでいくような作業をしました。その結果、大変効率的にデバッグが出来ました。「ベンチマーク機能、マジで神!」と思いました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ #include #include #include #include #include "misc.h" #include "position.h" #include "search.h" #include "thread.h" #include "tt.h" #include "ucioption.h" using namespace std; static const vector "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10", "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11", "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19", "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14", "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14", "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15", "r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13", "r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16", "4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w - - 1 17", "2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b KQ - 0 11", "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16", "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22", "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18", "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22", "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26", "6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b - - 0 1", "3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w - - 0 1", "2K5/p7/7P/5pR1/8/5k2/r7/8 w - - 0 1", "8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w - - 0 1", "7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w - - 0 1", "8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w - - 0 1", "8/1p3pp1/7p/5P1P/2k3P1/8/2K2P2/8 w - - 0 1", "8/pp2r1k1/2p1p3/3pP2p/1P1P1P1P/P5KR/8/8 w - - 0 1", "8/3p4/p1bk3p/Pp6/1Kp1PpPp/2P2P1P/2P5/5B2 b - - 0 1", "5k2/7R/4P2p/5K2/p1r2P1p/8/8/8 b - - 0 1", "6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1", "1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1", "6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1", "8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1" }; /// benchmark() runs a simple benchmark by letting Stockfish analyze a set /// of positions for a given limit each. There are five parameters; the /// transposition table size, the number of search threads that should /// be used, the limit value spent for each position (optional, default is /// depth 12), an optional file name where to look for positions in fen /// format (defaults are the positions defined above) and the type of the /// limit value: depth (default), time in secs or number of nodes. // benchmark()は文字通りStockfishの与えられたそれぞれのlimit(思考時間設定)での // 局面の解析による単純なベンチマークを実行する。5つのパラメーターがある。 // 1. 置換表サイズ // 2. 使用されるスレッド数 // 3. それぞれの局面に対して用いられるlimit値(optional(付けたくなければつけなくて良いの意味)でありdefaultは深さ13)、 // 4. FENフォーマットで調べるべき局面が書かれたoptional file name(defaultでは上で定義されている局面である)、 // 5. limitの型 : depth(default) , 秒数 もしくは ノードの数 // ※ 3.で指定している数値の型。"depth"以外に"time","nodes","mate"が指定できる。 void benchmark(const Position& current, istream& is) { string token; Search::LimitsType limits; vector // Assign default values to missing arguments string ttSize = (is >> token) ? token : "32"; string threads = (is >> token) ? token : "1"; string limit = (is >> token) ? token : "13"; string fenFile = (is >> token) ? token : "default"; string limitType = (is >> token) ? token : "depth"; Options["Hash"] = ttSize; Options["Threads"] = threads; TT.clear(); if (limitType == "time") limits.movetime = 1000 * stoi(limit); // movetime is in ms else if (limitType == "nodes") limits.nodes = stoi(limit); else if (limitType == "mate") limits.mate = stoi(limit); else limits.depth = stoi(limit); if (fenFile == "default") fens = Defaults; else if (fenFile == "current") fens.push_back(current.fen()); else { string fen; ifstream file(fenFile); if (!file.is_open()) { cerr << "Unable to open file " << fenFile << endl; return; } while (getline(file, fen)) if (!fen.empty()) fens.push_back(fen); file.close(); } int64_t nodes = 0; Search::StateStackPtr st; Time::point elapsed = Time::now(); for (size_t i = 0; i < fens.size(); ++i) { Position pos(fens[i], Options["UCI_Chess960"], Threads.main()); cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl; if (limitType == "perft") { size_t cnt = Search::perft(pos, limits.depth * ONE_PLY); cerr << "\nPerft " << limits.depth << " leaf nodes: " << cnt << endl; nodes += cnt; } else { // 思考開始 Threads.start_thinking(pos, limits, vector Threads.wait_for_think_finished(); nodes += Search::RootPos.nodes_searched(); } } elapsed = Time::now() - elapsed + 1; // Assure positive to avoid a 'divide by zero' cerr << "\n===========================" << "\nTotal time (ms) : " << elapsed << "\nNodes searched : " << nodes << "\nNodes/second : " << 1000 * nodes / elapsed << endl; } |