強くなったかどうかを自己対戦させて勝率を見るわけですが、将棋所のようなGUIをCPUの論理コア数だけ立ち上げて自己対戦させるのでは手間が馬鹿になりませんので、開発者は専用のツールを作ってそれで対局させています。やねうら王では、連続自己対戦フレームワーク、そして最近pythonで書き起こした、新連続自己対戦フレームワークを用意しています。
それで、これを用いて技巧との対局させて勝率を計測しようと思ったわけですが、いくつかの問題があって躓いたので、そのことについて簡単に書いておきます。
技巧のプロセスが残る問題
対局を途中で中断したときに、技巧のプロセスが残ります。原因と修正方法は以下のようになります。
『技巧』のバグ(?)見つけたので報告しといた。というか、『技巧』はソースコード自体は非常に洗練されているのだが、Stockfishに倣わず独自に書き起こしてある部分の挙動、色々動作が怪しい部分がある。https://t.co/OIjcwIckJ0
— やねうら王 (@yaneuraou) August 17, 2016
ReceiveCommands()なのですが、readline()の返し値を見てforループを抜ける構造になっていますが、このときquit処理をしないのでプロセスが終了しないです。
Windows環境でCtrl+Z、Linux環境でCtrl+Dが送られてきたときにreadline()は0を返すのですが、このときにプロセスが残って困ります。あるいは、Pythonなどからsubprocessとして起動したときに、親側が死んだときにsubprocess側にはEOFが送られてくるのですが、このときもreadline()の返し値は0になるので、このときに正常に終了しないとプロセスが残って困るのです。
修正案として、getlineの返し値を見て、”quit”コマンドが送られてきたと解釈するのがお手軽だと思います。
1 2 3 |
for (std::string command; ;) { if (!std::getline(std::cin, command)) command = "quit"; |
やねうら王では80並列対局しているので、プロセスが80個残ります。さすがにタスクマネージャーを起動して手で80個終了させるのはしんどいですからね。
技巧からの出力がpython側から細切れにしか取れない問題
私のpython側のコードが悪い気もしますが、これをpython側で回避するのは難しいため、技巧のほうを修正してビルドしました。
原因は、技巧が出力をprintfでやっていて、printfではbufferingせずに出力されるので、これをpython側からsubprocessのpipe経由で取得しようとしたときに、細切れの出力を拾ってしまうようです。以下のように、coutを経由して出力するように変更したところ、うまく取得できるようになりました。(もっといい方法があるのであればコメント欄で教えてください。)
1 2 3 4 5 6 7 |
#include #define SYNCED_PRINTF(...) { \ g_synced_printf_mutex.lock(); \ char _BUF[4096]; \ std::sprintf(_BUF,__VA_ARGS__); \ std::cout << _BUF; \ g_synced_printf_mutex.unlock(); } |
printfのバッファリングなしは珍しいなと思って調べてみました。
技巧はprintfのバッファリングモードを以下のようにバッファリングなし(_IONBF)に指定しているようでした。
std::setvbuf(stdin, NULL, _IONBF, 0);
他には、フルバッファリング(_IOFBF)か
行バッファリング(_IOLBF)が指定できるようです。
ご参考) http://www9.plala.or.jp/sgwr-t/lib/setvbuf.html
ほほー!そう言えば、将棋所の思考エンジンのサンプルで、そこバッファリングなしにしないと標準入力を親プロセス側でリダイレクトするときに受け取れないときがある、みたいなことが書いてあったのですが、どうもVisual Studio 2013あたりでランタイムが修正されたのか、そこをバッファリングなしにするとうまく動かないようで、いまどきの思考エンジンはすべてその変更はしてないです。技巧はprintf()を使っているのでたまたまうまく動いているのか…何なのか…。
エンジンとの間の入出力にそのような事情があったのですね(以下にも記載がありました)。
・将棋所 標準出力のバッファリングについて
http://www.geocities.jp/shogidokoro/enginecaution.html
将棋所としては新し目のVisual Studioを除いて、基本的にはなしを推奨したいようですね。
個人的には、usiが1行単位で意味をなすプロトコルであるとすれば(詳しくなくて断言できない)、行バッファリングが妥当と感じた1件でした。(個人の主観です。感じ方には個人差があります。)
将棋所のページ、そんなところが更新されてたんですね…。知りませんでした。
> usiが1行単位で意味をなすプロトコルであるとすれば
はい。USIプロトコルは常に1行単位ですね。私も行単位でバッファリングされていて良いと思います。
片側だけバッファリングするのはやさしさ(謎
taskkill /F /IM /T
でまとめて強制終了できそうですよ
taskkillというコマンドをいま知りました。なるほど…。