Windows上でメモリ確保にLarge Pageを使うとランダムアクセスが5%程度速くなるそうです。やねうら王の場合、ランダムアクセスはわりと支配的なのでTT(置換表)と、EvalHash(評価関数の値をcacheしておくメモリ領域)と評価関数テーブルをすべてLargePageに割り当てることで10数%高速化することがわかりました。
ここにきて、いまさら10数%も高速化するのか…と正直、驚いております。
Large Pageを使うC++のサンプルコード
- 置換表にLarge Pageを使うように変更。(やねうら王のGitHub)
https://github.com/yaneurao/YaneuraOu/commit/f726457d03ccd3c51332b8cee8254a7d2a80dc69
Large Pageを使うC++のサンプルコードを探し求めてこのページに辿り着かれた方は、上のリンク先がそのcommitとなっていますので、ご確認ください。
Large PageはOSのデフォルトでオフ
しかし、Large Pageの割当てはOSのデフォルトでオフになっており、簡単には使わせてもらえません。
WindowsでLarge Pageを有効にする方法
グループポリシーエディター(gpedit.msc)で、以下のようにセキュリティ設定を変更する必要があります。セキュリティポリシーを変更する内容なのであくまで自己責任でお願いします。
ちなみに、Windows 10 Homeですとグループポリシーエディタ(gpedit.msc)がデフォルトでは使えないので、「Windows 10 Home gpedit.msc」などでググって使えるようにしてください。
- グループポリシーエディター(gpedit.msc)を起動する
- ローカルコンピューターポリシー → コンピュータの構成 → Windowsの設定 → セキュリティの設定 → ローカルポリシー
→ ユーザー権利の割り当て → メモリ内のページのロック - ここで適切なユーザーに権限を付与する。
- OSを再起動する
参考記事)
Enable the Lock Pages in Memory Option (Windows)
https://msdn.microsoft.com/en-us/library/ms190730.aspx
Large Pageは癖が強い
また、Large Page、色々と癖があり、しばらく確保~開放を繰り返していると、メモリが断片化してくるのか、確保に失敗するようになります。なかなか癖の強い感じで、とても使いづらいです…。
2 MB の連続領域 5 つぐらい,普通はどこかにあるだろうという気がしますが,物理メモリの断片化は意外と厄介で,手元の Vista 環境では OS 起動直後でないと確保できませんでした.起動後しばらく放っておくと,合計 10 MB の Large Page ですら 0x000005aa エラーで確保できない有様です.断片化恐るべし.また,特権に関しても色々とややこしい事情が絡んでいるように思います.
Large Page として確保したメモリは,物理メモリ上に「Lock」されたという扱いで,一般的なワーキングセットからは除外されています.実際,Large Page として確保したメモリは,タスクマネージャ等に表示されるワーキングセットサイズに含まれていません.同じような理由で,Large Page として確保したメモリは,絶対にページファイルへ退避されません.必ず物理メモリ上に存在します.Large Page の確保に SE_LOCK_MEMORY_NAME 権限が必要なのはこのためと考えられますWindows で Large Page は「使える」か?(NyaRuRuが地球にいたころ)
https://nyaruru.hatenablog.com/entry/20080423/p1
まとめ
とりあえず、まあ、やねうら王(V4.91以降)でLarge Pageに対応することにしました。ということで。
ランダムアクセスをたくさん行うプログラムを高速化したい方は、一度試してみてはいかがでしょうか。
Large Pageは癖が強いと書かれてますがメモリを24GBも積んでおけば確保に失敗するようなことは、Windows起動後2、3日使った後でも無いですよ。
メモリ8GBだと確保失敗が時々起こっていましたが…
同じメモリサイズのLarge Pageを確保して、他のアプリケーションをあまり起動せずに開放するのを繰り返すだけでしたらメモリの断片化は起きにくいのではないかと。
やねうら王の場合、置換表、EvalHash、評価関数テーブルの3つにLarge Pageを使用していて、それぞれサイズが異なるので何らかメモリ断片化が起きるのかも知れません。
(実際、開発時にときどき確保できなくなり、再起動を余儀なくされることがあります..)
うーん、自分が開発している時はメモリ8GBしか積んでなかったときは、頻繁に確保できなくなってよく再起動していましたが、24GBに増設してからは4年ほどで1,2度しか起こってないですね。
単純に自分の使い方がメモリ断片化しにくい使い方だっただけかもしれません。
もしかしてOSの違いのせいかもしれません。
Windows7ではシャットダウン→起動でメモリがクリアされるのに対して、Windows10ではシャットダウン→起動ではクリアされてないような挙動(シャットダウン時にメモリイメージをストレージに保存して起動時に読みだしている?)をしているように見えますので…
(実際Windowsがどのような動作をしているかは知りません)
私のほう、開発時にブラウザやらVisual Studioやらを立ち上げているので、それらがメモリを動的に消費していき、Large Pageで確保している物理メモリが断片化していくような気はしています(´ω`)
想定シナリオとしては、Large Page 1GB 確保→ 1GB開放した直後にブラウザ等が動的にそこから1ページ分だけメモリをallocしたとして、この1GBあった連続メモリは 1GB – 4KB(1 page) に縮退しているのでもはやそこから1GBを確保することはできません。(´ω`) これが繰り返されるとあっという間に..。
NNUEの評価関数も効くんですか。なるほど。
評価関数テーブル、私の環境ではLarge Pageにするとわずかながら効果があるようでした。丸ごとL2/L3 cacheに載ってれば微差かも知れませんけども、それでもLarge Pageですとアドレス変換か何かが端折れるらしく、1,2%の効果はあるようで。また学習部で確保しているメモリもここから確保するようにしてあるので、そちらは効果があるような。(まだ、比較等はしていません)
ネストしすぎたせいか返信できなくなったのでこちらに書きます。
現状のx64版WindowsでLargePageを使っている場合、1024MBをLargePageで確保した場合、1024MBの連続領域は必要ないはずです。必要なのは2MBにアライメントされた2MBの連続領域が512個のはずです。(1GBPageのLargePageが実装されたら話は変わってきますが)、ただ問題が起こるまで少し長くなるだけで、そのような使い方であれば断片化は起こりそうな気がします。
あと、1GB – 4MB(1 page)は1GB – 4KB(1 page)の書き間違いですよね?
4MBは、書き間違えました(^^ゞ
> LargePageで確保した場合、1024MBの連続領域は必要ないはずです
そうなんですか?連続してないと、仮想アドレス→物理アドレスへのアドレス変換が必要になるので嫌な感じはしますが…。とりま、以下のドキュメント(古い?)には、”must be contiguous”とありますね。
https://docs.microsoft.com/ja-jp/windows/win32/memory/large-page-support
each large pageをどう解釈するかの問題だと思います。最初の方のEach large-page translation uses a single translation buffer inside the CPU.から考えるとeach large pageはLarge PageでのPage Table1エントリ分を指していると考えられ、そのPage Table1エントリ分が管理している2MBは連続した領域が必要ということだと思います。(明示はされてませんがアドレス変換の仕組みを考えたらアライメントされている必要もある)
なおLarge Pageでもアドレス変換はしています。
現在x64ではPage Tableの1エントリーが管理するサイズとして、4KB・2MB・1GBが選べます。通常は4KBを使っているのを2MBを使うことをLargePageと呼んでるだけです。
まあどういう実装になっているかは実験してみれば簡単にわかるような気も…。
例)
1. 搭載メモリの80%程度のLarge Pageを確保する。
2. 別アプリでメモリをn[GB]ほど確保する
3. 1.のメモリを開放する
4. 別のアプリでメモリをn[GB]ほど確保する(これで2.と併せて断片化される可能性が高い)
5. 再度1.
ここのコメントで予言した通り、本当に魔改造して草
http://yaneuraou.yaneu.com/2020/02/28/%e3%82%84%e3%81%ad%e3%81%86%e3%82%89%e7%8e%8b-v4-89%e5%85%ac%e9%96%8b%e3%81%97%e3%81%be%e3%81%97%e3%81%9f/
やねうら王の最適化ってどうすればできますか?
(depth 34に最適化など、、、)
探索部の最適化(探索パラメーターのチューニング)のことでしたら、↓に似た手法をとってます。
http://www2.computer-shogi.org/wcsc27/appeal/tanuki-/appeal.pdf
KPE9、KPE4だったり色々出てきてますが、やねさんの評価はどうですか?
NNUEの改良型のは全部全結合になってますけど、さすがにもうちょっとやりようがあるような…という感想です。
Large Page有効の状態で定跡を掘ると、大体40000局面程度掘ったところで必ずやねうら王が落ちてしまいます(3回連続で再現)。イベントログをみたところ、毎回0xc0000005で強制終了となっていました。Large Pageを使わないバージョンでは問題ないです。
これはもうLarge Pageというものの特性上起こり得ることであり、定跡を掘る際には無効にしたほうが無難ということでしょうか? また、Large Page使用・非使用をやねうら王側で毎回選択することは難しいのでしょうか?
Windows10Pro、物理メモリ64GBのPCで、USI_Hashは48GB、EvalHashは1GB割り当てています。
ちなみに、教師局面生成のときにはかなり長時間回しても落ちなかったので、EvalHashがいけないような気がしないでもないのですが……。EvalHash 0でやれば回避できる可能性は上がりますかね……?
LargePageオンであっても、起動時に確保するだけなのでアクセス違反で落ちるのは、あれれ?という感じでございます。
LargePageで確保できないときは普通の方法で確保していますし…。
なので、それは何らか別の要因のような気はします。(やねうら王の公式のGitHubにあるソースコードではなく、それを独自に取り込んだコードで、それがバグっている等)
なるほど……。Large Page Usedと出るのでLarge Pageできちんと確保はしていて、そこは問題ないのですね。
確かに純正やねうら王ではなくmブランチでのことなので、どこかしらのコードで相性等あるのかもしれませんし、単純におま環かもしれないのでもう少し使ってみます。
ちゃんと、やねうら王のNNUEまわりに行ったLarge Pageのコードがそちらに反映されてないのでしょうね。
どこかしら不十分なのかもしれませんね。とりあえず、関係がありそうな部分のコードをできるかぎり本家遵守にしてビルドし直して様子を見てみます。ありがとうございました。
source/eval/nnue/ フォルダをWinMergeなんかで差分取ってみるといいような。