今回はmisc.h/cppの解説です。このmisc.cppで実装されているロギング機構はなかなか興味深いhackによって実現されています。
ロギングのためにstd::cin/coutに対する入出力をファイルにリダイレクトしたいことがあるわけですが、既存のコード部分には手を加えずに、これを簡単に実装する方法があるのです。
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 |
/* 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 */ #ifndef MISC_H_INCLUDED #define MISC_H_INCLUDED #include #include #include #include "types.h" // 思考エンジンのバージョン文字列を返す // to_uciがtrueならば、id authorを用いて、作者名も出力する。 extern const std::string engine_info(bool to_uci = false); // prefetch()は与えられたアドレスの内容をL1/L2 cacheに事前に読み込む。 // これはnon blocking関数であり、メモリから読み込まれるデータのためのCPUの待機(これは極めて遅い)で // ストールさせない。 extern void prefetch(char* addr); // Logの書き出しを開始する。 // ※ これをtrueにすると標準入出力がログファイル"io_log.txt"にリダイレクトされる。 extern void start_logger(bool b); // 主に実行時の統計情報を集めるのに使われるデバッグ用の関数群 // ※ 置換表のhitした回数などの統計情報をとるための関数。 // ※ 現在使っていないが、デバッグのときに使うと良いと思われる。 // hit[0] = この関数が呼び出された回数。(母数) , hit[1] = dbg_hit_on(true)が呼び出された回数 extern void dbg_hit_on(bool b); // cがtrueのときだけdbg_hit_on(b)が呼び出される extern void dbg_hit_on_c(bool c, bool b); // means[0] = 母数 , means[1] = Σv extern void dbg_mean_of(int v); // 以上で集めた統計情報をcerrに出力。 extern void dbg_print(); // ログの書き出し用wrapper。実体はofstream // ※ ログはファイル名固定で、追記で書きだす。 // ※ 必要になったときにファイルをopen()して、書き出してすぐにclose()するようにして使うようだ。 // そのため、globalなインスタンスは存在しない。それに対して、Loggerというログ書き出し用のクラスもある。 // そちらはmisc.cppにて定義されている。 // ※ このクラスは探索ログの書き出しのために使用されているようだ。 struct Log : public std::ofstream { Log(const std::string& f = "log.txt") : std::ofstream(f, std::ios::out | std::ios::app) {} ~Log() { if (is_open()) close(); } }; namespace Time { typedef int64_t point; // システム時間をミリ秒単位に変換したもの。経過時間などを計測するときに使う。 point now(); } // Entry型をSize個(2の累乗個)だけ用意して、ハッシュキーによってこのSize個にアクセスして使うタイプの // ハッシュテーブル。これは、pawns.h / material.hで用いてある。 template struct HashTable { HashTable() : e(Size, Entry()) {} // k = 局面のハッシュキー Entry* operator[](Key k) { return &e[(uint32_t)k & (Size - 1)]; } private: std::vector }; // coutを同期化するために、io_lockとio_unlockというのをoperator << で // ostreamに放り込んだときにlock~unlockされるようにするための仕組み。 // ※ sync_cout << "TEST" << sync_endl; // のように必ず最後にsync_endlを出力してunlockしないといけないので注意。 enum SyncCout { io_lock, io_unlock }; std::ostream& operator<<(std::ostream&, SyncCout); #define sync_cout std::cout << io_lock #define sync_endl std::endl << io_unlock #endif // #ifndef MISC_H_INCLUDED |
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 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
/* 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 "thread.h" using namespace std; using namespace std::chrono; /// Version number. If Version is left empty, then compile date, in the /// format DD-MM-YY, is shown in engine_info. // Versionナンバー。もしVersionが左が空になっているならば、コンパイル日をDD-MM-YY形式にしたものが // 思考エンジン情報として表示される。 static const string Version = "DD"; /// engine_info() returns the full name of the current Stockfish version. This /// will be either "Stockfish /// the program was compiled) or "Stockfish /// Version is empty. // engine_info()は、現在のStockfishのバージョンをフルネームで返す。 // これは、"Stockfish // 日付である)もしくは、"Stockfish // 空であるかどうかに依存する。 // ※ Versionが空ならば前者。 // to_uciがtrueのときは、UCIコマンドのid authorを用いて作者名を表示する。 const string engine_info(bool to_uci) { const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); string month, day, year; // __DATE__ の部分はコンパイル時に得られる文字定数で、"Sep 21 2008"のようなフォーマット。 stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008" s << "Stockfish " << Version << setfill('0'); if (Version.empty()) { date >> month >> day >> year; s << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2); } s << (Is64Bit ? " 64" : "") << (HasPopCnt ? " SSE4.2" : "") << (to_uci ? "\nid author ": " by ") << "Tord Romstad, Marco Costalba and Joona Kiiski"; return s.str(); } /// Convert system time to milliseconds. That's all we need. // システム時間をミリ秒単位に変換する。これが我々の必要なもののすべてである。 Time::point Time::now() { return duration_cast } /// Debug functions used mainly to collect run-time statistics // 主に実行時の統計情報を集めるのに使われるデバッグ用の関数群 static uint64_t hits[2], means[2]; // hit[0] = この関数が呼び出された回数。(母数) , hit[1] = dbg_hit_on(true)が呼び出された回数 void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; } // cがtrueのときだけdbg_hit_on(b)が呼び出される void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); } // means[0] = 母数 , means[1] = Σv void dbg_mean_of(int v) { ++means[0]; means[1] += v; } // 以上で集めた統計情報をcerrに出力。 void dbg_print() { if (hits[0]) cerr << "Total " << hits[0] << " Hits " << hits[1] << " hit rate (%) " << 100 * hits[1] / hits[0] << endl; if (means[0]) cerr << "Total " << means[0] << " Mean " << (double)means[1] / means[0] << endl; } /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We /// can toggle the logging of std::cout and std:cin at runtime while preserving /// usual i/o functionality and without changing a single line of code! /// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81 // 我々のロギング機構。ここにおけるトリックは、cin.rdbuf()とcout.rdbuf()をcin/coutからfile streamに // 結び付けられた2つのTie objectで置換することである。 // 我々は、std::coutとstd::cinのロギングする/しないを実行時に切り替えることができ、 // 通常のi/o機能を保存し、1行のコードの修正もなしである。 // アイデアは http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81 struct Tie: public streambuf { // MSVC requires splitted streambuf for cin and cout Tie(streambuf* b, ofstream* f) : buf(b), file(f) {} int sync() { return file->rdbuf()->pubsync(), buf->pubsync(); } int overflow(int c) { return log(buf->sputc((char)c), "<< "); } int underflow() { return buf->sgetc(); } int uflow() { return log(buf->sbumpc(), ">> "); } streambuf* buf; ofstream* file; int log(int c, const char* prefix) { static int last = '\n'; if (last == '\n') file->rdbuf()->sputn(prefix, 3); return last = file->rdbuf()->sputc((char)c); } }; // ログの書き出し用のwrapper class Logger { Logger() : in(cin.rdbuf(), &file), out(cout.rdbuf(), &file) {} ~Logger() { start(false); } ofstream file; Tie in, out; public: // logの書き出しを開始する。 // misc.hのstart_logger()を呼び出したときにこれが呼び出される。 // b == trueだと書き出しモード。 // falseだと書き出さないモード static void start(bool b) { static Logger l; // いまと違うモードにするのであればファイルをopen / close if (b && !l.file.is_open()) { l.file.open("io_log.txt", ifstream::out | ifstream::app); cin.rdbuf(&l.in); cout.rdbuf(&l.out); } else if (!b && l.file.is_open()) { cout.rdbuf(l.out.buf); cin.rdbuf(l.in.buf); l.file.close(); } } }; /// Used to serialize access to std::cout to avoid multiple threads to write at /// the same time. // std::coutへのシリアライズアクセス(operator << によるアクセス)で複数スレッドからの // 同時書き出しを避けるために用いられる。 std::ostream& operator<<(std::ostream& os, SyncCout sc) { static std::mutex m; if (sc == io_lock) m.lock(); if (sc == io_unlock) m.unlock(); return os; } /// Trampoline helper to avoid moving Logger to misc.h // misc.hにLoggerを移動させるのを回避するためのトランポリンヘルパー。 // ※ ここをクッションとして呼び出すの意味か? // これをtrueにすると標準入出力がログファイル"io_log.txt"にリダイレクトされる。 void start_logger(bool b) { Logger::start(b); } /// prefetch() preloads the given address in L1/L2 cache. This is a non /// blocking function and do not stalls the CPU waiting for data to be /// loaded from memory, that can be quite slow. // prefetch()は与えられたアドレスの内容をL1/L2 cacheに事前に読み込む。 // これはnon blocking関数であり、メモリから読み込まれるデータのためのCPUの待機(これは極めて遅い)で // ストールさせない。 #ifdef NO_PREFETCH void prefetch(char*) {} #else // プリフェッチ用のヘルパ // あとで使うメモリを事前に指定してcacheさせておく。 void prefetch(char* addr) { # if defined(__INTEL_COMPILER) // This hack prevents prefetches to be optimized away by // Intel compiler. Both MSVC and gcc seems not affected. // このhackはIntel compilerの最適化によってprefetch命令が取り去られるのを防止する。 // MSVCとgccには影響はないと思われる。 __asm__ (""); # endif # if defined(__INTEL_COMPILER) || defined(_MSC_VER) _mm_prefetch(addr, _MM_HINT_T0); # else __builtin_prefetch(addr); # endif } #endif |