オセロを速く解く話/solveothello

 オセロを速く解く話/solveothello

京大マイコンクラブの春合宿で、オセロを速く「解く」ことについて特化して話したスライドです。

42d1548e2aefcd213edc270da3ae7203?s=128

prime number

March 15, 2019
Tweet

Transcript

  1. 14.

    16 NegaMax 法 -1 倍して最大値を取る +8 -4 +6 -8 +20

    +4 +16 先手 後手 終局 -6 +8 -20 -4 -16
  2. 15.

    17 NegaMax 法 -1 倍して最大値を取る +4 +8 -4 +6 -8

    +20 +4 +16 先手 後手 終局 -8 +4
  3. 21.

    23 NegaAlpha 法 NegaMax 法と同様に手番が変わるたびに -1 倍する 子ノードの α 値は

    -beta 、 β 値は -alpha になる 子ノードの結果が beta 以上なら枝刈りできる 実装が簡単になる 計算量はほとんど同じ
  4. 29.

    31 ベンチマーク mini-max alpha-beta fastest-first 0.01 0.1 1 10 100

    1000 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
  5. 30.

    32 ベンチマーク mini-max alpha-beta fastest-first 0.01 0.1 1 10 100

    1000 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 1 〜 35 倍程度の高速化
  6. 31.

    33 ビットボード オセロの盤面は 8*8=64 マス 各マスは空き、黒、白のいずれか これを 64bit 整数 2

    つで持つ 自分の石のある位置、相手の石のある位置 64bit*2=128bit=16Byte で盤面を表現できる メモリ使用量の削減になる 手番の交代は 2 つの整数の入れ替えで表現できる
  7. 40.

    42 ビットボードでひっくり返る石の計算 反対側の石 -1 とマスクの AND を取る ひっくり返る石の位置がわかる 0 1

    0 0 0 0 0 0 1 1 1 1 1 0 0 0 マスク 反対側の石 0 0 1 1 1 1 1 1 0 0 1 1 1 0 0 0 反対側 -1 反転する石
  8. 43.

    45 ベンチマーク mini-max alpha-beta fastest-first bit-board 0.01 0.1 1 10

    100 1000 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
  9. 44.

    46 ベンチマーク mini-max alpha-beta fastest-first bit-board 0.01 0.1 1 10

    100 1000 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 5 倍程度の高速化
  10. 46.

    48 ベンチマーク mini-max alpha-beta fastest-first bit-board leaf-opti 0.01 0.1 1

    10 100 1000 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
  11. 47.

    49 ベンチマーク mini-max alpha-beta fastest-first bit-board leaf-opti 0.01 0.1 1

    10 100 1000 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 2 倍程度の高速化
  12. 48.

    50 SIMD/ ビット演算命令 SIMD(Single Instruction Multiple Data) 1 命令で複数データを処理すること SSE,

    AVX, NEON など ビット演算命令 特定のビット演算を高速化する命令 ひっくり返る石の計算、着手可能位置の計算に利用
  13. 49.

    51 ベンチマーク mini-max alpha-betafastest-first bit-board leaf-opti avx2-popcnt 0.01 0.1 1

    10 100 1000 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
  14. 50.

    52 ベンチマーク mini-max alpha-betafastest-first bit-board leaf-opti avx2-popcnt 0.01 0.1 1

    10 100 1000 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 2 倍程度の高速化
  15. 51.

    53 NegaScout 法 αβ 探索では Alpha 値以下のノードには興味がない 何も更新されないので 先頭以外の各子ノードについて、 alpha

    値を超えるかどうかだけ探索 solve(next, -alpha-1, -alpha) で実現できる 超えるなら改めて完全探索 solve(next, -beta, -alpha) する
  16. 52.

    54 ベンチマーク 0.01 0.1 1 10 100 1000 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
  17. 53.

    55 ベンチマーク 0.01 0.1 1 10 100 1000 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 1 〜 3 倍程度の高速化
  18. 55.

    57 ベンチマーク 0.01 0.1 1 10 100 1000 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
  19. 56.

    58 ベンチマーク 0.01 0.1 1 10 100 1000 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 1 〜 1.7 倍程度の高速化
  20. 69.

    71 ベンチマーク 0.01 0.1 1 10 100 1000 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
  21. 70.

    72 ベンチマーク 0.01 0.1 1 10 100 1000 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 1 〜 10 倍程度の高速化 (#39 は除く )
  22. 72.

    74 ベンチマーク 0.01 0.1 1 10 100 1000 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
  23. 73.

    75 ベンチマーク 0.01 0.1 1 10 100 1000 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 5% 程度の高速化
  24. 76.

    78 計算量の見積もり 大体 1 〜 5*10^7nodes/sec 出ている ゲーム木の大きさは 10^54 、局面数は

    10^28 程度 あくまで推定だか、大きくは外れていないと思われる αβ 探索は、理想的な場合はノード数が平方根ぐらい 置換表を使えば 10^14 ぐらい読めば良い…? 単純計算で 3*10^6sec=35 日程度
  25. 83.

    85 GPU の利用 並列計算なら CPU より並列計算に適したデバイスがある GPU (Graphic Processing Unit)

    画像処理を行うプロセッサ PC で画面に映像を出すのに使われる 並列計算に向いている GPGPU General Purpose computing on GPU GPU を汎用計算に使うこと
  26. 84.

    86 GPU の利用 実装してみた https://github.com/primenumber/GPUOthello2 αβ 法 + 葉から遠いところで速さ優先探索 +

    静的並べ替え 再帰をループで実装する必要があり、かなり移植に苦労
  27. 85.

    87 GPU での分岐 GPU は何スレッドかを 1 つに束ねて実行している 16, 32, 64

    スレッドなど 束ねられたスレッドは同一の命令を実行する 分岐があった場合 分岐の方向が全部同じ場合 分岐の方向の命令だけ実行する 分岐の方向が違う場合 両方のパスをマスク付きで実行する
  28. 86.

    88 GPU での分岐 分岐の方向が同じ場合 if (X) { A; B; }

    else { C; D; } X==true X==true X==true X==true A A A A B B B B
  29. 87.

    89 GPU での分岐 分岐の方向が違う場合 if (X) { A; B; }

    else { C; D; } X==true X==false X==false X==true A A A A B B B B C C C C D D D D
  30. 88.

    90 GPU で再帰 + 分岐 GPU で再帰 + 各スレッドでバラバラに分岐すると、 1

    スレッド以外はほぼ常にマスクされている状態になる void f() { if (X) { f(); } else { A; } } X !X !X X !X X X X
  31. 89.

    91 GPU で再帰 + 分岐 GPU で再帰 + 各スレッドでバラバラに分岐すると、 1

    スレッド以外はほぼ常にマスクされている状態になる void f() { if (X) { f(); } else { A; } } X !X !X X !X X X X f() f() f() f() f()
  32. 90.

    92 GPU で再帰 + 分岐 GPU で再帰 + 各スレッドでバラバラに分岐すると、 1

    スレッド以外はほぼ常にマスクされている状態になる void f() { if (X) { f(); } else { A; } } X !X !X X !X X X X f() f() f() f() f() !X X !X X X
  33. 91.

    93 GPU で再帰 + 分岐 GPU で再帰 + 各スレッドでバラバラに分岐すると、 1

    スレッド以外はほぼ常にマスクされている状態になる void f() { if (X) { f(); } else { A; } } X !X !X X !X X X X f() f() f() f() f() !X X !X X X f() f() f()
  34. 92.

    94 GPU で再帰 + 分岐 GPU で再帰 + 各スレッドでバラバラに分岐すると、 1

    スレッド以外はほぼ常にマスクされている状態になる void f() { if (X) { f(); } else { A; } } X !X !X X !X X X X f() f() f() f() f() !X X !X X X f() f() f() X !X !X
  35. 93.

    95 GPU で再帰 + 分岐 GPU で再帰 + 各スレッドでバラバラに分岐すると、 1

    スレッド以外はほぼ常にマスクされている状態になる void f() { if (X) { f(); } else { A; } } X !X !X X !X X X X f() f() f() f() f() !X X !X X X f() f() f() X !X !X f()
  36. 94.

    96 GPU で再帰 + 分岐 GPU で再帰 + 各スレッドでバラバラに分岐すると、 1

    スレッド以外はほぼ常にマスクされている状態になる void f() { if (X) { f(); } else { A; } } X !X !X X !X X X X f() f() f() f() f() !X X !X X X f() f() f() X !X !X f() !X
  37. 95.

    97 GPU で再帰 + 分岐 GPU で再帰 + 各スレッドでバラバラに分岐すると、 1

    スレッド以外はほぼ常にマスクされている状態になる void f() { if (X) { f(); } else { A; } } X !X !X X !X X X X f() f() f() f() f() !X X !X X X f() f() f() X !X !X f() !X A
  38. 96.

    98 GPU で再帰 + 分岐 GPU で再帰 + 各スレッドでバラバラに分岐すると、 1

    スレッド以外はほぼ常にマスクされている状態になる マスクされている間は計算していないのと同じ 演算能力の 1/16 や 1/32 しか使えない
  39. 97.

    99 GPU での実装 αβ 法を素直に実装すると、再帰 + 分岐になる 各スレッドがバラバラに分岐して CPU より遅くなる

    そこで、スタックをメモリ上に用意し、再帰をループに 再帰はスタックを用いてループで書けることが知られている ループを用いると一重の分岐コストだけで済む 約 5*10^9nodes/sec NegaScout 等が実装されていないので、 nodes/sec の比ほどは速くはならない
  40. 99.

    101 PEZY-SC/SC2 PEZY Computing 社の開発している MIMD プロセッサ 各スレッドが完全に独立して動作する 分岐によるペナルティがない! PEZY-SC/SC2

    の搭載されている菖蒲 (/SystemB) スパコ ンを使わせてもらえることになった 2017 年 12 月(ちょうど社長が逮捕された頃)
  41. 100.

    102 PEZY-SC/SC2 版ソルバー 実装してみた https://github.com/primenumber/PEZY-Othello 8*10^9 nodes/sec (on PEZY-SC2 1

    モジュール ) スタック領域が 1 スレッドあたり 2KB/2.5KB しかないため、かなり移植に苦労した
  42. 101.

    103 PEZY-SC/SC2 版ソルバー αβ 法 + 葉から遠いところで速さ優先探索 + 静的並べ替え +

    置換表 +NegaScout 評価関数の利用はまだうまく行っていない MIMD 動作のため、速さ優先探索の分岐ペナルティが なくなり、かなり葉の近くまで速さ優先探索出来るので nodes/sec の違い以上に速い
  43. 103.

    105 PEZY-SC/SC2 の問題点 スループットを出すにはかなりの並列度が必要 1 モジュールあたり数十万局面必要 1CPU に 4 〜

    8 モジュール繋がっているので全体では 数百万局面必要 1 局面を解くのにかかる時間(レイテンシ)は長い 700MHz, 4 スレッド交代のため
  44. 107.

    109 オセロソルバー on FPGA Othello ソルバーと FPGA は相性が良い FPGA にとって苦手な演算がいらない

    乗算器の数も余裕がある ビット演算などが得意 オセロ専用回路を作れる
  45. 108.

    110 オセロソルバー on FPGA 2018 年 5 月〜 https://github.com/primenumber/FPGAOthello 現在の仕様

    パイプラインプロセッサ、 9 ステージ 9cycle / node を 9 並列 @1 コア 100MHz 程度 数千 LUT&FF / 1 コア 愚直な αβ 法
  46. 109.

    111 FPGA 版の実装 FPGA では再帰をそのまま回路に落とし込むのは難しい スタックを Block RAM 上に作ってループで解く またか…

    パイプライン化するためには工夫が必要 すべての実行パスでサイクル数を揃える ステートマシンが単純なループになるようにする Block RAM へのアクセス回数を減らす 2 ポートしかないため
  47. 110.

    112 オセロソルバー on FPGA のアーキテクチャ 9 段パイプライン Fetch Decode1 Decode2

    Exec1 Exec2 Exec3 Exec4 Check Write Stack [154bit width * (16*9) depth] Dual Port BRAM
  48. 116.

    118 オセロソルバー on FPGA の性能 AWS F1 インスタンスに載っている、 Xilinx UltraScale+

    VU9P で考える 2,364K FF & 1,182K LUT だいたい 300 コアぐらいは載りそう? 3*10^10 nodes/sec 程度
  49. 117.

    119 オセロソルバー on FPGA の今後の展望 Fetch ステージでスタックへの書き込みをやめる 次の Write ステージで書き込めば原理上行ける

    Simple Dual Port RAM にできる アーキテクチャによっては BRAM の数を減らせる Fetch/Decode と Exec/Check/Write を分ける Fetch/Decode を高速に動かす Exec は半分くらい演算器が遊んでいるので有効活用したい 速さ優先探索等を行う・クロック周波数の向上
  50. 124.

    126 ベンチマーク @4 コア 8 スレッド 0.01 0.1 1 10

    100 1000 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
  51. 125.

    127 ベンチマーク @4 コア 8 スレッド 0.01 0.1 1 10

    100 1000 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 1 〜 3 倍程度の高速化 (#47 等は除く )
  52. 127.

    129 YBWC vs APHID コア数が少ない時: YBWC の方がよい コア数が多い時: APHID の方がよいはず…

    現状では CPU 版 : YBWC GPU/PEZY-SC2 版 : APHID の亜種 非同期にタスクを投げるのが難しいので亜種 FPGA 版 : APHID の予定
  53. 128.

    130 現状の進捗 6x6 オセロ 初手からの 32 手完全読み CPU 版(評価関数なし) :

    1 時間 34 分 GPU 版 : 43 分 PEZY-SC2 版 : 未実装( GPU 版と同程度?) FPGA 版 : 未実装( GPU 版よりかなり速いはず…) 目標性能が出れば 1 秒未満で解けるが…