Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
オセロを速く解く話/solveothello
Search
prime number
March 15, 2019
Programming
2
2.2k
オセロを速く解く話/solveothello
京大マイコンクラブの春合宿で、オセロを速く「解く」ことについて特化して話したスライドです。
prime number
March 15, 2019
Tweet
Share
More Decks by prime number
See All by prime number
Ray Tracing In One Hour
primenumber
0
57
プログラムを高速化する話Ⅱ ~GPGPU編~
primenumber
1
530
プログラムを高速化する話
primenumber
0
310
コンピュータで問題をサッと解きたい!
primenumber
0
260
Other Decks in Programming
See All in Programming
CSC509 Lecture 09
javiergs
PRO
0
140
NSOutlineView何もわからん:( 前編 / I Don't Understand About NSOutlineView :( Pt. 1
usagimaru
0
330
Snowflake x dbtで作るセキュアでアジャイルなデータ基盤
tsoshiro
2
520
Click-free releases & the making of a CLI app
oheyadam
2
110
GitHub Actionsのキャッシュと手を挙げることの大切さとそれに必要なこと
satoshi256kbyte
5
430
Amazon Bedrock Agentsを用いてアプリ開発してみた!
har1101
0
330
Content Security Policy入門 セキュリティ設定と 違反レポートのはじめ方 / Introduction to Content Security Policy Getting Started with Security Configuration and Violation Reporting
uskey512
1
520
광고 소재 심사 과정에 AI를 도입하여 광고 서비스 생산성 향상시키기
kakao
PRO
0
170
3 Effective Rules for Using Signals in Angular
manfredsteyer
PRO
0
100
C++でシェーダを書く
fadis
6
4.1k
.NET のための通信フレームワーク MagicOnion 入門 / Introduction to MagicOnion
mayuki
1
1.3k
Duckdb-Wasmでローカルダッシュボードを作ってみた
nkforwork
0
120
Featured
See All Featured
Building a Scalable Design System with Sketch
lauravandoore
459
33k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
47
2.1k
Embracing the Ebb and Flow
colly
84
4.5k
Product Roadmaps are Hard
iamctodd
PRO
49
11k
Adopting Sorbet at Scale
ufuk
73
9.1k
Writing Fast Ruby
sferik
627
61k
Large-scale JavaScript Application Architecture
addyosmani
510
110k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
25
1.8k
Reflections from 52 weeks, 52 projects
jeffersonlam
346
20k
Being A Developer After 40
akosma
86
590k
Building Better People: How to give real-time feedback that sticks.
wjessup
364
19k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
26
2.1k
Transcript
オセロを速く解く話 KMC6 回生 prime @2019 年 KMC 春合宿
2 自己紹介 KMC-ID prime そすうぽよ Twitter: @_primenumber Mastodon: @
[email protected]
poyo
鯖新規登録受付中! GitHub: @primenumber
5 概要 オセロを速く「解く」ことに注力します 強いオセロプログラムを作ることが目標ではない 結果的に強いオセロプログラムは作れる CPU だけでなく様々なハードウェアを駆使して高速化
6 目次 オセロを解くとは CPU で解く GPU で解く PEZY-SC/SC2 で解く FPGA
で解く 並列化アルゴリズム
7 オセロを解くとは あるオセロの盤面が与えられたときに、両プレイヤーが 最善を尽くしたときの試合結果を求めること → 最善を尽くすとは?
8 最善を尽くすとは 再帰的に定義される ゲームが終了している盤面→なにもしない(自明) それ以外→手番のプレイヤーが 1 手プレイしたあとの盤面 それぞれについて、両者が最善を尽くした場合の結果を 求め、そのうち手番のプレイヤーにとって最も良い結果と なる盤面に遷移するプレイをする
9 最善を尽くす例 丸が各盤面、矢印がプレイ(ゲーム木という) 各数字は石差 +6 -8 +20 +4 +16 先手
後手 終局
10 最善を尽くす例 後手は後手にとって最も有利な局面に遷移する -8 +4 +6 -8 +20 +4 +16
先手 後手 終局
11 最善を尽くす例 先手は先手にとって最も有利な局面に遷移する +4 -8 +4 +6 -8 +20 +4
+16 先手 後手 終局
12 最善を尽くす例 両者が最善を尽くすと先手の 4 石勝ち +4 -8 +4 +6 -8
+20 +4 +16 先手 後手 終局
13 MiniMax 法 オセロなどの二人零和有限確定完全情報ゲームを解く アルゴリズムの1つ 定義に従い再帰的に最善手を求めるアルゴリズム ゲーム木の大きさにおおよそ比例した時間がかかる → ゲーム木は残り石数が増えると爆発的に大きくなるので 残り石数が増えるごとに計算時間も爆発的に大きくなる
14 NegaMax 法 MiniMax 法では自分の手番では最大値、相手の手番 では最小値を計算する必要がある 手番によって実装を変える必要がある 手番が変わるごとに試合結果を -1 倍して計算する
常に最大値を計算すれば良くなる 実装が簡単になる 計算量はほとんど同じ
15 NegaMax 法 +6 -8 +20 +4 +16 先手 後手
終局
16 NegaMax 法 -1 倍して最大値を取る +8 -4 +6 -8 +20
+4 +16 先手 後手 終局 -6 +8 -20 -4 -16
17 NegaMax 法 -1 倍して最大値を取る +4 +8 -4 +6 -8
+20 +4 +16 先手 後手 終局 -8 +4
18 NegaMax 法の実装
19 FFO ベンチマーク コンピューターオセロ界で最も有名なベンチマークテスト FFO#1 から #79 まであり、おおよそ番号順に難しく なっていく FFO#1
でも 14 石空きであり、人間が読み切るのは かなり難しい
20 FFO#1, #2 NegaMax 探索だと最も簡単な部類でも時間がかかる #1 #2 探索時間(秒) 498.901 265.216
21 AlphaBeta 法 順番に読んでいると、読まなくても良い分岐があることが 赤の盤面を評価している時、その子ノードについて 12 より良い盤面は相手は選ばない 20 より悪い盤面は自分は選ばない つまりこの盤面は
選ばれない +20 +20 +12 +32 +12 +20
22 AlphaBeta 法 選ばれない盤面なら読まなくても試合結果には影響しない この盤面の探索をここで打ち切ることが出来る 局面を読む順番に依存するが、大幅な高速化が見込める 実際の実装では、 alpha 値と beta
値という2つの 値を持つ 「 alpha 値と beta 値の間にだけ興味がある」という意味
23 NegaAlpha 法 NegaMax 法と同様に手番が変わるたびに -1 倍する 子ノードの α 値は
-beta 、 β 値は -alpha になる 子ノードの結果が beta 以上なら枝刈りできる 実装が簡単になる 計算量はほとんど同じ
24 NegaAlpha 法の実装
25 NegaAlpha 法の効果 #1 #2 mini-max (秒) 498.901 265.216 alpha-beta
(秒) 0.776 0.63
26 NegaAlpha 法の効果 #1 #2 mini-max (秒) 498.901 265.216 alpha-beta
(秒) 0.776 0.63 数百倍高速化
27 CPU での高速化テクニック 速さ優先探索 ビットボード 葉の近くに対する最適化 SIMD/ ビット演算命令 NegaScout 法
置換表 評価関数の利用
28 速さ優先探索 真ん中の赤の局面を読む時、探索を途中で打ち切る ためには +20 以下の局面を見つければ良い 最善手である必要はない 最も読む局面数が少ない局面から 読みたい +20
+20 +32 +20
29 速さ優先探索 着手可能手が少ない局面から順番に読む 着手可能手の数は読む局面数と正の相関があると 考えられるので、読む局面数が少ないと考えられる 速さ優先探索という Fastest-First Heuristic +20 +20
+32 可能手 5 可能手 3 可能手 9 +20
30 速さ優先探索 着手可能手が少ない局面から順番に読む 着手可能手の数は読む局面数と正の相関があると 考えられるので、読む局面数が少ないと考えられる 速さ優先探索という Fastest-First Heuristic +20 +20
+32 可能手 5 可能手 3 可能手 9 +20
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
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 倍程度の高速化
33 ビットボード オセロの盤面は 8*8=64 マス 各マスは空き、黒、白のいずれか これを 64bit 整数 2
つで持つ 自分の石のある位置、相手の石のある位置 64bit*2=128bit=16Byte で盤面を表現できる メモリ使用量の削減になる 手番の交代は 2 つの整数の入れ替えで表現できる
34 ビットボード ある場所に石を置いた際にひっくり返る石の計算 今の盤面で石を置ける場所の計算 両方とも分岐なしで算術演算とビット演算で出来る
35 ビットボードでひっくり返る石の計算 簡単のため横 1 列、左向きの計算を説明 × 印の場所に白石を置いた時
36 ビットボードでひっくり返る石の計算 ビットボードでの表現 1 0 1 1 1 0 1
0 0 1 0 0 0 0 0 1 黒石 白石
37 ビットボードでひっくり返る石の計算 左端の黒石は今後の計算に邪魔なのでなくす 0 0 1 1 1 0 1
0 0 1 0 0 0 0 0 1 黒石 白石
38 ビットボードでひっくり返る石の計算 置く場所より左が 1 のマスクを作る 0 0 1 1 1
0 1 0 0 1 0 0 0 0 0 1 黒石 白石 1 1 1 1 1 0 0 0 マスク
39 ビットボードでひっくり返る石の計算 マスクの反転と黒石の OR を取る 必要な範囲の黒石の位置だけ取り出す 0 0 1 1
1 0 1 0 0 1 0 0 0 0 0 1 黒石 白石 1 1 1 1 1 0 0 0 マスク 0 0 1 1 1 1 1 1 演算結果
40 ビットボードでひっくり返る石の計算 さっきの結果に +1 する 置く位置から黒石がどこまで連続するかわかる 0 0 1 1
1 0 1 0 0 1 0 0 0 0 0 1 黒石 白石 0 1 0 0 0 0 0 0 結果 0 0 1 1 1 1 1 1 結果 +1
41 ビットボードでひっくり返る石の計算 白石、マスクと AND を取る 挟む反対側の石の位置がわかる(挟めなければ 0 になる) 0 1
0 0 0 0 0 0 0 1 0 0 0 0 0 1 白石 1 1 1 1 1 0 0 0 マスク 0 1 0 0 0 0 0 0 結果 +1 反対側の石
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 反転する石
43 ビットボードでひっくり返る石の計算 右方向も似たような方法で求められる 8*8 盤面でもマスクを使うと同様にして求められる 横方向だけでなく縦や斜め方向も求められる
44 ビットボードで着手可能位置の計算 これができると速さ優先探索が高速になる これも各方向に分けて高速に計算することができる
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
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 倍程度の高速化
47 葉の近くに対する最適化 ゲーム木の葉の近く(終局間際)では 速さ優先探索はコストが大きい 着手可能位置の計算や、ソートのコストが大きい そこで、残り石数によって速さ優先探索するかどうか分岐 残り 6 石以下ではナイーブな探索に切り替え
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
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 倍程度の高速化
50 SIMD/ ビット演算命令 SIMD(Single Instruction Multiple Data) 1 命令で複数データを処理すること SSE,
AVX, NEON など ビット演算命令 特定のビット演算を高速化する命令 ひっくり返る石の計算、着手可能位置の計算に利用
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
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 倍程度の高速化
53 NegaScout 法 αβ 探索では Alpha 値以下のノードには興味がない 何も更新されないので 先頭以外の各子ノードについて、 alpha
値を超えるかどうかだけ探索 solve(next, -alpha-1, -alpha) で実現できる 超えるなら改めて完全探索 solve(next, -beta, -alpha) する
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
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 倍程度の高速化
56 置換表 異なる打ち方から同じ盤面に到達することがある 同じ盤面を何度も探索する代わりに表に読んだ結果を保存 2 回目からは表を参照するだけで計算が終わる 実際には αβ 探索なので真の結果の範囲しかわからない 範囲を保存して今後の探索に活かす
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
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 倍程度の高速化
59 評価関数の利用 αβ 探索は、局面を読む順番に依存するが高速
60 評価関数の利用 αβ 探索は、局面を読む順番に依存するが高速
61 評価関数の利用 αβ 探索は、局面を読む順番に依存するが高速 自分に有利な局面から読んだほうが良いことが多い alpha 値が大きくなってよりたくさん枝刈りできるため
62 評価関数の利用 速く読むためには、子ノードの結果が欲しい 子ノードの結果を知るためには、実際に読む必要がある 循環している…
63 評価関数の利用 子ノードの結果の近似値を使うことで解決する 近似値によって並べ替える 近似値の計算 f( 盤面 )= そのノードを最後まで読んだ結果の近似値 となるような
f が欲しい f を評価関数という 評価関数の作り方はいくつもあるが、精度の高いものを紹介
64 評価関数の作り方 要は盤面の「良さ」を評価したい 良い盤面ならプラス、悪い盤面ならマイナスになる プレイヤーにとって有利・不利な点を見つける
65 例:辺の評価 辺の石の配置によって有利・不利があると言われている 山:有利 ウィング:不利 爆弾:一概には言えない
66 評価関数の作成 辺のありうるパターンは 3^8=6561 通り そのそれぞれに点数を付ける 山は +100 、ウィングは -100
など 4 辺の点数の和を盤面の評価値とする
67 より良い評価関数 辺だけでなく隅の周辺や、縦横斜めの列にも点数をつける 評価値は各パターンに対する点数の和 x x x x x x
x x x x x x x x x x x x x x x x x x x x x x
68 より良い評価関数 点数の付け方も人力でやるのは大変 盤面と実際に完全読みを行った結果の組をたくさん用意 100 万組ぐらいは欲しい 評価関数の評価値と完全読みの結果が近くなるように学習 実際にはスパースな線型方程式を解くだけで出来る
69 評価関数の適用 パターンによる評価関数の計算はそれなりに重い 残り数石なら最後まで読んでしまったほうが速いし正確 速さ優先探索との兼ね合いもある 12 石以上空いているときに採用する
70 評価関数の適用 20 石空きの局面に対する評価関数の評価より 15 石空きの局面に対する評価関数の評価のほうが正確 かと言って毎回 5 手分読んでいては時間がかかりすぎる 最初に何手か評価関数の値を用いて浅い
αβ 探索をする 探索時の結果をテーブルに保存しておき、完全読みに使う
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
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 は除く )
73 葉に対する最適化その 2 1 石空きかどうかで場合分けをして最適化する 少しだけ効果がある
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
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% 程度の高速化
76 実装 https://github.com/primenumber/rust-reversi Rust で実装
77 オセロを解く 20 石空きぐらいは簡単に読めるようになった 時間をかければ更に空きマスが多くても解ける 初手( 60 石空き)から解けるか…?
78 計算量の見積もり 大体 1 〜 5*10^7nodes/sec 出ている ゲーム木の大きさは 10^54 、局面数は
10^28 程度 あくまで推定だか、大きくは外れていないと思われる αβ 探索は、理想的な場合はノード数が平方根ぐらい 置換表を使えば 10^14 ぐらい読めば良い…? 単純計算で 3*10^6sec=35 日程度
79 オセロを解く 実際に 35 日計算機を回す 残念ながら解けない 10^14 のメモリはない αβ 法にとって理想的な条件ではない
80 オセロを解く 読む必要のあるノード数を見積もるのは難しい 10^15 程度なら 1 年程度走らせればよいが… 10^16, 10^17,… ぐらいあったら…?
最悪死んでしまう また、 10 年 100 年プログラムを動かし続けるのは難しい
81 オセロを解く どうしようもないのか…?
82 オセロを解く どうしようもないのか…? 待って! 我々はまだ CPU の一部しか使っていない
83 マルチスレッド化 サーバー用 CPU を使えば数十スレッド並列実行可能 単純計算で数十倍速くなる 1 〜 2*10^9nodes/sec 程度
84 並列に解く とりあえず、一つの局面を並列化して読むのではなく、 多数の局面を並列に読むことを考える 単に 1 局面あたり 1 並列で解く アルゴリズムが単純になる
一つの局面を並列化するのはあとで考える
85 GPU の利用 並列計算なら CPU より並列計算に適したデバイスがある GPU (Graphic Processing Unit)
画像処理を行うプロセッサ PC で画面に映像を出すのに使われる 並列計算に向いている GPGPU General Purpose computing on GPU GPU を汎用計算に使うこと
86 GPU の利用 実装してみた https://github.com/primenumber/GPUOthello2 αβ 法 + 葉から遠いところで速さ優先探索 +
静的並べ替え 再帰をループで実装する必要があり、かなり移植に苦労
87 GPU での分岐 GPU は何スレッドかを 1 つに束ねて実行している 16, 32, 64
スレッドなど 束ねられたスレッドは同一の命令を実行する 分岐があった場合 分岐の方向が全部同じ場合 分岐の方向の命令だけ実行する 分岐の方向が違う場合 両方のパスをマスク付きで実行する
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
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
90 GPU で再帰 + 分岐 GPU で再帰 + 各スレッドでバラバラに分岐すると、 1
スレッド以外はほぼ常にマスクされている状態になる void f() { if (X) { f(); } else { A; } } X !X !X X !X X X X
91 GPU で再帰 + 分岐 GPU で再帰 + 各スレッドでバラバラに分岐すると、 1
スレッド以外はほぼ常にマスクされている状態になる void f() { if (X) { f(); } else { A; } } X !X !X X !X X X X f() f() f() f() f()
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
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()
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
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()
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
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
98 GPU で再帰 + 分岐 GPU で再帰 + 各スレッドでバラバラに分岐すると、 1
スレッド以外はほぼ常にマスクされている状態になる マスクされている間は計算していないのと同じ 演算能力の 1/16 や 1/32 しか使えない
99 GPU での実装 αβ 法を素直に実装すると、再帰 + 分岐になる 各スレッドがバラバラに分岐して CPU より遅くなる
そこで、スタックをメモリ上に用意し、再帰をループに 再帰はスタックを用いてループで書けることが知られている ループを用いると一重の分岐コストだけで済む 約 5*10^9nodes/sec NegaScout 等が実装されていないので、 nodes/sec の比ほどは速くはならない
100 GPU の問題点 とはいえ、分岐のせいでそれなりに演算器は遊んでしまう GPU の構造上の問題なので、どうしようもない… GPU 以外の並列計算機を探さなければ…
101 PEZY-SC/SC2 PEZY Computing 社の開発している MIMD プロセッサ 各スレッドが完全に独立して動作する 分岐によるペナルティがない! PEZY-SC/SC2
の搭載されている菖蒲 (/SystemB) スパコ ンを使わせてもらえることになった 2017 年 12 月(ちょうど社長が逮捕された頃)
102 PEZY-SC/SC2 版ソルバー 実装してみた https://github.com/primenumber/PEZY-Othello 8*10^9 nodes/sec (on PEZY-SC2 1
モジュール ) スタック領域が 1 スレッドあたり 2KB/2.5KB しかないため、かなり移植に苦労した
103 PEZY-SC/SC2 版ソルバー αβ 法 + 葉から遠いところで速さ優先探索 + 静的並べ替え +
置換表 +NegaScout 評価関数の利用はまだうまく行っていない MIMD 動作のため、速さ優先探索の分岐ペナルティが なくなり、かなり葉の近くまで速さ優先探索出来るので nodes/sec の違い以上に速い
104 PEZY-SC/SC2 版の実装 スタック領域が狭い 再帰で実装すると 10 石ぐらいしか読めない 結局 GPU と同様にメモリ上にスタックを構築、ループ化
105 PEZY-SC/SC2 の問題点 スループットを出すにはかなりの並列度が必要 1 モジュールあたり数十万局面必要 1CPU に 4 〜
8 モジュール繋がっているので全体では 数百万局面必要 1 局面を解くのにかかる時間(レイテンシ)は長い 700MHz, 4 スレッド交代のため
106 さらなる高速化を目指して どういうアーキテクチャなら良い? 浮動小数点演算は不要 整数乗算も(いまのところ)いらない ビット演算、シフト、加算減算ばかり 理想的にはオセロ専用回路があると良い
107 そんなものはない 浮動小数点演算はともかく、整数乗算もカットしたような アーキテクチャは殆ど無い オセロ専用回路を載せたアーキテクチャなど当然ない
108 無いなら作る! FPGA Field-Programmable Gate Array の略 自分で自由に回路を組むことが出来る オセロ専用回路のあるプロセッサを作れる!
109 オセロソルバー on FPGA Othello ソルバーと FPGA は相性が良い FPGA にとって苦手な演算がいらない
乗算器の数も余裕がある ビット演算などが得意 オセロ専用回路を作れる
110 オセロソルバー on FPGA 2018 年 5 月〜 https://github.com/primenumber/FPGAOthello 現在の仕様
パイプラインプロセッサ、 9 ステージ 9cycle / node を 9 並列 @1 コア 100MHz 程度 数千 LUT&FF / 1 コア 愚直な αβ 法
111 FPGA 版の実装 FPGA では再帰をそのまま回路に落とし込むのは難しい スタックを Block RAM 上に作ってループで解く またか…
パイプライン化するためには工夫が必要 すべての実行パスでサイクル数を揃える ステートマシンが単純なループになるようにする Block RAM へのアクセス回数を減らす 2 ポートしかないため
112 オセロソルバー on FPGA のアーキテクチャ 9 段パイプライン Fetch Decode1 Decode2
Exec1 Exec2 Exec3 Exec4 Check Write Stack [154bit width * (16*9) depth] Dual Port BRAM
113 Fetch ステージ 前回のループの処理で 石を置いたとき・新たな探索がスタートしたとき 前回のループからデータが送られてくるので、 それをスタックに書き込む それ以外 スタックのトップから探索情報を読みこむ
114 Decode ステージ 探索情報を元に何をすべきか決める この局面を探索し終えた場合 パスの場合:パスの操作をする 終局の場合:石差計算をして親ノードに結果を伝える それ以外:親ノードに結果を伝える αβ 法の枝刈りが出来る場合
親ノードに結果を伝える それ以外 通常の探索をする
115 Exec ステージ まだ探索していない子ノードを探索したい 空きマスの中から一つ選ぶ 4 サイクル掛けてひっくり返る石の計算をする 通常の探索でない場合は何もしない
116 Check ステージ ひっくり返る石があるか確かめて、やることを決める ある場合 石を置いて子ノードの探索に移る ない場合 次の空きマスを調べる
117 Write ステージ 結果をスタックに書き込んだり、次のループに渡したり 石を置ける場合 石を置いたことをスタックに記録、次のループに子ノードを 渡す 置けない場合:その場所を調べたことをスタックに記録 終局の場合:スコアを次のループで親ノードに渡す などなど…
118 オセロソルバー on FPGA の性能 AWS F1 インスタンスに載っている、 Xilinx UltraScale+
VU9P で考える 2,364K FF & 1,182K LUT だいたい 300 コアぐらいは載りそう? 3*10^10 nodes/sec 程度
119 オセロソルバー on FPGA の今後の展望 Fetch ステージでスタックへの書き込みをやめる 次の Write ステージで書き込めば原理上行ける
Simple Dual Port RAM にできる アーキテクチャによっては BRAM の数を減らせる Fetch/Decode と Exec/Check/Write を分ける Fetch/Decode を高速に動かす Exec は半分くらい演算器が遊んでいるので有効活用したい 速さ優先探索等を行う・クロック周波数の向上
120 オセロソルバー on FPGA の問題点 速さ優先探索等をしていないのでそんなに速くない 将来的には実装したい たくさんのコアをつないだときのタスクの与え方 ラウンドロビンでタスクを与えるなどの工夫が必要 CPU
との接続 バスと FIFO を介して接続する…?
121 全体アーキテクチャ予想図 CPU 速さ優先探索コア 速さ優先探索コア Core Core Core Core Core
Core Core Core Core Core Core Core スイッチ
122 目標性能 10^12nodes/sec@AWS f1.16xlarge 8 モジュールの FPGA 搭載 300 コア
/ モジュール 2nodes/cycle >200MHz
123 並列化について とりあえず並列で速く解けるようになった やりたいことは1つの盤面について深く速く読むこと
124 αβ 探索の並列化 αβ 探索の並列化は難しい 枝刈りできるかどうかが過去の探索結果に依存するため 完全読みでなければ Lazy SMP というよい手法がある
今回は完全読みなので YBWC や APHID という手法を使う
125 YBWC あるノードを探索するとき、ノードが「良い」順番なら、 α 値の更新は先頭ノードでしか起こらない 仮定の元では、先頭ノード以外はどんな順番で探索しても 良い 先頭だけ直列で探索し、残りの探索を並列化する
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
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 等は除く )
128 APHID マスタースレッドとたくさんのワーカスレッドに分ける マスタースレッドは根に近い数段を探索する 根から一定段数進んだら、ワーカーにその局面の探索を 投げる マスタースレッド自身はワーカーの探索が終わるまでは 評価関数を用いた推定値を使って探索を続ける マスタースレッドは繰り返し探索し、推定値を使わなく なったら終了
129 YBWC vs APHID コア数が少ない時: YBWC の方がよい コア数が多い時: APHID の方がよいはず…
現状では CPU 版 : YBWC GPU/PEZY-SC2 版 : APHID の亜種 非同期にタスクを投げるのが難しいので亜種 FPGA 版 : APHID の予定
130 現状の進捗 6x6 オセロ 初手からの 32 手完全読み CPU 版(評価関数なし) :
1 時間 34 分 GPU 版 : 43 分 PEZY-SC2 版 : 未実装( GPU 版と同程度?) FPGA 版 : 未実装( GPU 版よりかなり速いはず…) 目標性能が出れば 1 秒未満で解けるが…
131 今後の展望 まずは有名な定石や 6x8 オセロを解く 評価関数の精度向上 より「賢く」評価関数を使う 並列化の効率向上 FPGA の利用
132 今後の展望 8x8 オセロを解く より精度の高い評価関数の作成( Deep Learning? ) 分散コンピューティング より賢い分散・並列化アリゴリズム
133 まとめ 様々なテクニックにより探索を高速化出来た いろいろなハードウェアを検討し、ソルバーを実装した CPU, GPU, PEZY-SC/SC2, FPGA 探索の並列化アルゴリズムを実装した アーキテクチャやアルゴリズムは改善の余地あり
今後のそすうぽよ先生の進捗にご期待ください!