FPGAによるコンピューター将棋 2012年の結論
ここ1ヶ月ぐらい更新をお休みしていたのは私のなかで一つの結論が出たからです。
その結論を書いても仕方ないかと思い、書かずにこのブログのことは放置しておりました。
ところが昨日、GA将!!!!!!さんにこのブログが見つかった(http://d.hatena.ne.jp/Gasyou/20121215/1355545292)のでここにその結論を書いていきます。
かなり長い話になるので順を追って説明していきますね。
Terasicから最新のFPGAを搭載したFPGAボードが発売された
まず、現段階で入手可能な内蔵メモリのそこそこ多い、最新のFPGAボードのお値段が発表されました。
DE5-Net FPGA Development Kit
http://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&CategoryNo=158&No=526
$8,000です。digikeyで70万円ぐらいでしょうか。そこそこお高いです。
内蔵メモリは3MB程度しかありません。当然いまどきのコンピューター将棋の評価関数テーブルは載りません。
評価関数のためにメモリを食いつぶすアプローチは失敗する
Bonanzaの評価関数のうち、ビットを圧縮したり、無駄な部分を取り除いたり、3駒関係のうち、駒が近接していなければ大きな値はつかないのでそういうのを端折ったり…まあいろいろ考えたのですが、そういうアプローチでは無理です。どうやっても載りません。
FPGAは製品サイクルがかなり長く、なかなか次の世代のFPGAが出ません。
Stratix V(が搭載されている評価ボード)は出たばかりなので、次の製品は2,3年後ぐらいになるでしょう。
2,3年後にいまの内蔵RAMが倍のサイズになったとしてもそれでもBonanzaの評価関数を載せるのはかなり厳しいものがあります。
(評価関数のためだけに内蔵RAMを使いきってしまうわけにもいきませんし…)
ゆえに、こういうアプローチではどうしようもならないことがわかります。
わかりますというか、わかりました。
A級リーグ指し手1号の製作後、その作者の伊藤さんがBonanzaの保木さんに「Bonanzaの評価関数そのままというわけにはいかないのですか?」と尋ねられたことがあったと思うのですが、いまですら内蔵RAM的には極めて厳しいので、当時なら当然全く話にもならないレベルだったでしょう。
よく知りませんので間違ったことを書いているかも知れませんが、FPGAの内蔵RAMとはSRAMです。SRAMはとても高価です。
IntelのCore i7のL1 cacheとか長らくコア当たり16KBから増えないところをみますと、SRAMを大きくするのは高くつくというのがわかるかと思います。
FPGAはそのSRAMが3MBも(!)載っているわけです。この時点で、FPGAが高価になるのは自明の理です。
2,3年後にいまの倍の内蔵RAMが載っているFPGAが発売になったとしても、お値段も倍ぐらいになっていることは想像に難くありません。
そこで評価関数のためにメモリが数MB規模で必要になるようなアプローチは3年ぐらい先まで見越したとしてもFPGAでは現実的ではないということがわかります。
省メモリな評価関数の設計
結局、3駒関係は無理、KKPも厳しいかも知れません。
しかしBonanzaの発表資料を振り返ってみるに、初代Bonanzaは10K程度の評価因子しかなかったそうです。
つまりその程度であっても1Mnps程度(?)の探索速度でR2400ぐらいにはなるということです。
そう考えますと全く希望がないわけではありませんね。
次に、GPS将棋の評価関数を1サイクルで求めるようなものを考えていたのですが回路規模が極めて大きくなります。
回路効率は全くよろしくありません。たぶん上記のDE5-Netでも到底載り切らないでしょう。デバッグもすこぶるしにくいです。
そこでアプローチを変えまして、将棋専用のCPUを64個ぐらい実装すればどうかと思ったのです。
FPGA型コンピューター将棋において汎用CPUを複数載せるアプローチは失敗する
まず最初に考えたのは汎用CPUを載せて、それらを並列的に使って評価関数を求めるという手法です。
AlteraはNios IIとopen sourceなCPUを公開しているのですがStratix V(最新のFPGA)において300MHz程度にしかなりません。
300MHzとはひどい話です。DMIPS値でCore i7と比較しますと1/100程度の値でしかありません。
Core i7でさえ、4GHz近く出ますし、うまく命令を選べば1クロックで2命令実行できたり(よく知りません)、アドレッシングが豊富なのでこの部分をうまく利用しますと1命令でも普通のRISCマシンの2,3命令分ぐらいの仕事が出来たりします。
そう考えますと、300MHzのCPUが20個ぐらいあって初めてCore i7の1コア相当ぐらいにしかなりません。
しかも並列的に評価関数の計算をしなくてはならないため、そのタスクを分散させるオーバーヘッドもあります。
こう考えますとどう頑張っても汎用CPUではCore i7にすら及ばないというのが見えてきます。
この結論に至った時点で私はかなり萎えまして、このブログを更新する気力すら失せていました。
将棋専用CPUを作ろう
そこで汎用CPUとは決別して将棋専用CPUを作るという発想に至ります。
汎用CPUに、81bit(将棋盤9×9)のBitboard演算を1命令でこなすような命令などを追加したものだと考えてください。
またそれぞれのCPUの出力を足し合わせる回路をそのCPUの外側につけてあるので評価値を足しあわせる部分の時間は無視して考えられるものとします。
評価関数をこのようなCPUに分散して担当させるということです。
幸い、GPS将棋のような評価関数というのは評価項目は比較的独立していますので、それぞれのCPUにタスクを割り振って、並列的に計算させること自体はそれほど難しいことではありません。
またデバッグがしにくくなるので、その将棋専用CPUのエミュレーターをPC側で実装して、そこでデバッグまで終わらせ、FPGAには最終的にそのエミュレーター用に書いていたコードを持っていくというアプローチが考えられます。この手法にはかなりの実現可能性があり、ある程度の成功も収めそうです。
ここまでで一応評価関数の問題は解決しそうです。
PC側で、将棋CPU向けの評価関数の開発をする作業にかなりの時間をとられるでしょうけど。
やはり難しい指し手生成
そうなってきますと指し手生成のほうがむしろ問題として浮上してきます。
評価関数の計算を将棋CPUに分散させることにしたので、将棋CPU自体は300MHz程度で動作させるとします。
しかし指し手生成回路はそんな速度で動作しません。
指し手のオーダリングのためにコンパレーター(比較器)を何段にも重ねているからです。
また、将棋盤の各升×(10方向+駒打7種)もの指し手を一気にコンパレーターで比較しようとするのは回路的にもすこぶる無駄です。
回路的に無駄なのはまあ仕方ないとしても、それをもう少し安価なFPGAボードで開発~動作テストできませんと、開発がままなりません。
いきなり$8,000の評価ボードを購入するわけにはいかないからです。
そこで、指し手生成にもさきほど評価関数の計算のために作成した将棋CPUを使い回すことを思いつきます。
つまり将棋CPUには指し手生成のための機能もつけます。
ところが、この部分がすごく難しいのです。
うまく将棋CPUに分散させて、そして現実的な時間で指し手生成が出来るかという問題です。
例えば、1段ごとに1CPUを割り当てて、9段で9個のCPUを割り当てるとします。各升で最大17種類の指し手があります。
まあ、17種類すべてがあるケースはまずないでしょうけど、7種の駒打ちと、大駒の移動などで1段に100種類ぐらいの指し手が生成されることはあるでしょう。
そうしますと、1クロックで1つの指し手を生成できたとしても100クロック要することになります。
将棋CPUが300MHzで動作しているとしまして、指し手生成以外の処理は一切要らないとしましても、このとき3Mnpsでしか動作しません。
ボトルネックになっていることは自明で極めて遅いということがわかります。
すべての指し手を都度生成するのは、回路規模を小さく保とうと考えたときに、この部分がボトルネックになりそうです。
ついでに指し手にスコアをつけてオーダリングもしたいわけですが、さきほども書きましたようにCore i7の何十分の1ぐらいの性能しかない汎用CPUでソートするわけにはいかず、ソートもハード的に特化した何らかのアプローチが必要になります。
A級リーグ指し手1号が100K LE程度で収まっていることを考えますと、たぶんすべての指し手を1クロックで生成し、スコアが最大の指し手を選ぶような回路を合成したとしてもおそらく200K LE程度で収まるのではないかとは思います。ちなみに上に書いた$8,000のFPGAボードに載っているFPGAは622K LE相当のようです。収まると言えば収まります。
つまり、
1) 毎回全指し手を生成し、指し手のスコアが最大のものを選ぶ
2) 指し手を生成し、ソートして、それをメモリに保存しておく
とどちらが良いアプローチかということです。
前者は回路規模が大きくなりますが、メモリに保存しておく必要はないのでシンプルな作りになるでしょう。
後者は回路規模は小さくなりそうですが、高速にソートするための回路を作るのはかなり難しく、そしてそれほど速くはないでしょう。
Bonanzaのソースコードなどに倣うとしますと、オーダリングは次のようにするのが普通かと思います。
a) 置換表の指し手
b) 捕獲する手、SEEの値順
c) Killer×1,2
d) スコア最大の手 上位2手
e) あとは総当り
指し手の全生成(駒を取らない指し手など)が必要になるのはd)のところなのですが、上位2手だけならソートする必要はなく、あとは都度生成するとしても、並び替える必要がないのであれば続きの手を1つ生成するだけで済みます。
しかし、こういうオーダリングですと探索深さで先細っていくような前向き枝刈りを考えたときに、ここがソートされていて欲しいというのはあります。
次の記事はStockfishのmarginに関しての記事ですが、前向き枝刈りの先細りに関しても同様の議論が成り立ちます。
[Stockfish] futility margins array
http://d.hatena.ne.jp/sakurapyon/20121214#1355453198
ただ、d)まで行ってβcutされていない状況ですと、もうあとはe)をすべて見てもβcutされない可能性は極めて高いはずで、前向き枝刈りのためにソートする価値があるのかどうか疑問ではあります。
あと余談ですが、FPGAでコンピューター将棋を実装する場合、b)のSEEは損でしょう。
ここはMVV-LVAにすべきだと思います。FPGAで作る場合、局面を進めたり戻したりするコストが馬鹿にならないからです。
並列的に指し手を生成するのであれば、将棋CPUそれぞれが勝手に局面を進めたり戻したり出来ると良いのですが、そういう作りにしようと思いますと、将棋CPUそれぞれが将棋盤を正確に(大駒の利きなども含めて)持っていないといけませんし、それぞれの将棋CPUが盤面情報を更新できないといけません。これは極めて大掛かりになます。
ともかく、どうやれば指し手生成をうまい形で並列化させられるか、そしてソートさせられるかという部分について私はまだ考え中でこの部分に関しましては結論らしきものは得ていません。
長々と書いてきたわりには中途半端ではありますが以上が2012年のFPGAによるコンピューター将棋の結論らしきものです。
FPGAによるコンピューター将棋をご検討の方の参考になりましたら幸いです。
Altera DE0 nanoで実装できるか?
DE0 nanoのSDRAMについて
DE0 nanoには32MBのSDRAMが搭載されています。Terasicの評価ボードのうち、これだけ大容量のSDRAMが搭載されているモデルは珍しく、その点はなかなか良いと思うのです。
出来れば、DE0 nanoで動作速度は遅くとも一通り思考ルーチンが動作すれば今後の開発意欲も湧いてくるというものです。
Nois IIに専用命令を拡張していき実装することを考えていたのですが、Nois IIは論理合成にそこそこ時間がかかるのでターンアラウンドタイムが非常に時間が悪くなってしまいます。まだverilog初心者の私が採るべき方法ではないと判断しました。
そこでまずはDE0 nanoのSDRAMについて少し調べてみます。
DE0 nanoに搭載されているSDRAMのデータシートはこちら。
ftp://ftp.altera.com/up/pub/Altera_Material/11.0/Tutorials/Verilog/DE0-Nano/Using_the_SDRAM.pdf
データバスは16bitのようで、16bitずつしか読み書きできないようですが…置換表は128bitの読み書きがしたいのでどうせならばこのSDRAM8個ついてくれているとすごく良かったのですが…ついてないものはどうにもなりませんね。
ピンヘッダー自体は出ていますからSDRAMのドーターボードがあれば良いのですが…何故か売られていないようです。
指し手生成がボトルネックになる?
局面を進めたあと、置換表を参照しに行く必要があり、通常、そのメモリアクセスがボトルネックになるでしょうから、その間に指し手生成をしておけば指し手生成に要する時間は相対的には無視できるのかも知れません。(よくわかりません)
ただ、局面を1つ戻して(探索深さを1つ戻して)次の指し手について調べるようなときに、その再度すべての指し手生成が必要になり、このときは置換表を調べに行く必要がないのでこの間の時間もすこぶるもったいないです。
未生成の指し手のうちオーダリングで2番目に来るべき指し手についてもピックアップできていれば良いのですが、コンパレーターツリーの出力付近に2番目がいるとは限らず(早い段階で1番目の値と比較され、出力付近まで残れていない可能性があります)、2番目を求めるためのコンパレーターツリーを別途用意するかという話になるのかも知れません。それとも、次の局面に進め、置換表を見に行っている時間に指し手生成部では局面を進めずに2番目の指し手を生成だけやっておくのはアリなのではないでしょうか。
もしそういうことが可能なのであれば、結局、置換表への読み書き部分がボトルネックであるということになるのかも知れません。置換表に評価値が書かれていないときは評価値を評価関数で計算しますが、しかしそれは置換表を調べに行く程度の時間でおそらく出来るはずです。
探索部も考えてみますと…
・ある局面を展開したとき、最初に置換表を調べる
case 1) 置換表に載っている → 既知の局面なのでまず置換表の指し手を試す。置換表の指し手を生成済みにしたあと指し手生成をしておく。そのあと局面を進める。置換表は先行して調べておく。
case 2) 置換表に載っていない → 未知の局面なので評価関数を呼んで評価値を取得。その間に指し手生成。評価値によってはβcutがなされる。
・ある局面に戻ってきたとき
case 3) 枝刈りの条件を満たしている → 局面をさらに戻る。置換表に書き出すならば書き出す。またbest moveとして生成した指し手のhistoryを加点する(指し手生成器がこの点数を保持している)
case 4) 枝刈りの条件を満たしていない → 次の指し手を生成する必要がある。(ここに時間がかかる。他に何もできない!馬鹿らしい!)
つまりはcase 4でストールするわけです。ここでストールする割合がどれくらいあるのかよくわかりませんが結構あるような気がします。複数コアで探索していれば、置換表の読み書き自体がボトルネックになるでしょうからそうでもないと思うのですが、LEが余っているのであれば複数コアにするよりは少しでも評価関数の充実を図りたいのでcase 4が許せないのです。
case 4の割合がどれくらいあるのか考えてみますと、置換表の指し手がある場合は普通その手で枝刈りがされますから、たいてい遅延表に指し手が書かれていなかったケース、すなわち新規局面なのだと思うのですが、新規局面は全体の半分以上が新規局面だと思うので、この部分が時間がかかりますと非常に苦しいものがあります。
こうして見ますと2番目の指し手も同時に生成してしまうのはアリではないかと思えてきます。2番目の値を抽出するコンパレーターをうまく書く方法がよくわからないのですが、N個ずつ組みにして値を比較して、上位2個ずつ残していけば、最後に残るのは上位2個ですかね…。コンパレーターツリー、何段必要なのでしょうか。わけがわかりません。簡単にできそうにないですね。これをやるぐらいなら、指し手生成をもっと並列化するほうが現実味がありそうです。
そんなわけでして、指し手生成は極めて難しいということは前回の考察から理解できたのですが、まずはDE0 nanoに収まるように書きたいので、まずは81セルの指し手生成器を用意して17クロック固定で使用して6段のコンパレーターツリーで実装してみるところからやってみたいと思います。
Noisが150MHz程度で動作するところを見ますと(5段パイプラインですが…)、6段のコンパレーターツリーと言えども100MHz程度では動作するのでしょうから(なんなら分割すれば…)、指し手生成が17+3クロック程度で終わるとしまして…他の処理に要する時間は相対的に無視できるとしまして…。5Mnps程度。DE0 nanoで(このLE数で合成できるかどうかは知りませんが)5Mnps程度!
いまどきたいした数字ではないのでしょうけども、1Mnpsでもなんでも動けばこっちのものです。あとは指し手生成をさらに並列化するなり、評価関数を充実させるなり、もっと速いFPGAボードに移行するなり、探索コアを複数載せるなり、なんでもできます。
そのためには動くことを第一段階目の目標にせねばなりません。
DE0 nanoに搭載されているSDRAMについて
SDRAMの型番 → ISSI IS42S16160B-7
データシート
http://pdf1.alldatasheet.com/datasheet-pdf/view/153633/ISSI/IS42S16160B-7B.html
100MHz動作でCAS latency = 2のようです。つまりはREADコマンドが発行されたクロック信号の立ち上がりから数えて、2つ目のクロック信号の立ち上がりでデータがもらえるようです。
つまりは、最悪でも1/50M秒でデータが取り出せていると考えていいわけですかね。8回の読み出しには10回要することになりますね。置換表を1ノードに1回、読み書きするならばこのメモリの読み書きに要する時間だけでおおよそ1/5M秒。ああ、置換表への書き出しはそのノードの評価値が確定したときのみですので、それよりはマシだとしまして…それでも1/10M秒。指し手生成などが時間0としましても10Mnpsしか出ません。なかなかメモリアクセスだけでも大変ですね。
SDRAMが8つ載っているならおおよそ1/8で済むのでしょうけども。16bitずつの読み書きはさすがに遅すぎますね。内蔵RAMにcacheすることも考えられますが、そんなに訪問済みのノードばかりに当たるわけでもなくその部分ではそんなに効率化は図れないでしょうね。なんとももどかしいものがあります。
コンピューター将棋に適したFPGAボード
Altera DE4 Development and Education Board
http://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&CategoryNo=138&No=501
Terasicの評価ボードには、Stratix IVが載っていてSO-DIMM(64bit幅で転送できるみたい?)が2本載っているものがあります。お値段$2,995。228KLE,FPGA内蔵メモリ17,133Kbit(おおよそ2MB)。Stratix IVとVはさほど速度に違いはないようですし、内蔵メモリ的にも現時点で最大級です。LE的にもちょうどいい感じだと思いますし、この評価ボードならばうまくすれば50Mnpsぐらい出せるのではないでしょうか。
FPGA用コンピューター将棋で一番難しいのは指し手生成
Hydraの解説を読んでいて、だいたい作りが理解できました。
1) 探索は状態マシンで、たいした状態数ではないので普通に実装すれば問題なさげ。
2) 評価関数はFPGAらしく並列で計算して問題なさげ。
3) 指し手に従って局面を進める / 戻す
4) 指し手生成~オーダリング~1手選択
1),2)をパイプライン化することで性能は変わりそうですが、それはあとでもやろうと思えば出来るでしょうし、そこは難しい部分ではないのであまり問題にはならないです。3) は簡単なので説明を要しません。
問題は4)ですね。いかにして超並列的に指し手を生成し、オーダリングして1手選択するか。どうもコンピューター将棋やチェスではこの部分の設計が非常に難しく、そしてボトルネックになりうるようです。
半面、評価関数のほうは、評価因子をいくら増やそうと内蔵メモリに評価関数テーブルが載る限りはルックアップは並列化できますし、どんどん増やすことが出来ます。
ゆえに、指し手生成を並列的に高速に出来た時点でFPGAのコンピューター将棋のうちの半分は完成したようなものではないかと思います。(PC版のコンピューターをFPGA化することを前提に話をしていますので、評価関数および探索アルゴリズム自体はすでに確定しているものとします)
Hydraプロジェクトに学ぶ
http://fpgashogi.hateblo.jp/entry/2012/11/17/200418
Hydraプロジェクトに学ぶ
FPGAによるコンピューターチェスとしてHydraプロジェクトがあります。
Hydraプロジェクト
http://direct.xilinx.co.jp/xcell/xl53/jp53xcell_23.pdf
現在、67のブロックRAM、9,879のスライス、5,308のTBUF、534のフリップフロップ、18,403のLUTが使用されています。探索ノードあたりのサイクル数の上限は9サイクルです。純粋なソフトウェア ソリューションでは、少なくとも6,000 Pentiumサイクルが必要だと推定されています。VirtexTM-I 1000上では、最長パスは51のロジック レベルで構成され、このデザインは30 MHzで動作します。プログラムが50 MHzで動作できるようにするため、デザインをVirtex XC2VP70-5に移植したところです。
FPGA実習課題 16種
verilog HDLについてはある程度勉強が終わって、簡単なCPUなら設計できそうというところまで来ました。このあとNios IIをやってもいいのですが、どうせならNios IIのソースコードも読みたいですし、Nios IIのカスタム命令も作りたいです。しかしいきなり複雑なことをやると論理合成~デバッグに時間がかかり、効率が悪そうです。
そこで、FPGAで何か簡単なプログラムを16個書いてみて、それから考えることにしよう!と思いました。16個というのはキリが良いのでこの数字にしました。
1) LEDに結果(数値など)を表示するmodule(デバッグのために使いたい)
2) ブロックRAM・分散メモリ読み書き
3) コンパレーター
4) エンコーダー
5) Stack / Queueの実装
6) SRAM読み書きのテスト
7) SDRAM読み書きのテスト
// 他、思いついたら追加します
16) パイプライン型の32bit RISC型CPUの実装
// やってみたあとに追記していきます。
FPGA入門 その2
FPGA関連でつまずいたことをトピック別に書いていきます。
・ブロックRAMと分散メモリとの違い
いまさら聞けない FPGA入門
http://monoist.atmarkit.co.jp/mn/articles/0609/20/news118.html
[引用ここから]
ブロックRAM・乗算器」は、論理を実現するためのリソースです。図3のように回路内全体に均一的にブロックRAM・乗算器が提供されることで、ユーザーはFPGA内での回路の位置に制限を受けることなく、演算処理とデータの保持ができます。
なお、ロジックセル内のルックアップテーブルは、ロジックの実現だけでなく小型のメモリ(4入力のLUTは16bitメモリ)として使うこともできます。従って、ブロックRAM以外に局所的にメモリを増やすことができます。この場合は、LUTを「分散型メモリ」と呼びます。
[引用ここまで]
・波形ビュアー
GTKWave
http://linuxcom.info/gtkwave.html
[引用ここから]
[引用ここまで]
verilog入門 完全版
Verilogに関しては入門書を買わずにネット上の情報だけでなんとかしようと思います。 勉強に使ったURLを書いていきます。簡単な順番に並べてあります。
Verilog-HDL入門
http://cas.eedept.kobe-u.ac.jp/~arai/Verilog/
→ 文法のところだけ流し読み しました。
初めてでも使えるVerilog HDL文法ガイド ―― 記述スタイル編
http://www.kumikomi.net/archives/2009/07/verilog_hdl.php