思うだけで学ばない日記 2.0

思うだけで学ばない日記から移転しました☆!よろしくお願いします。

Core i7想定でキャッシュの使い方検討

キャッシュ制御の件は想像だけでは如何ともし難いと渋々認めざるを得なくなってきたので下記資料を拾って拾い読みした

プリフェッチ

まず、プリフェッチはかなりテキトーに解釈される(プロセッサに対するヒント扱い)らしく、別にキャッシュをロックとかしないっぽい

プロセッサは、見込み的な読み込みが許されるメモリ・タイプ(すなわち、WB、WC、およびWTメモリ・タイプ)が割り当てられたシステム・メモリ領域から、いつでもデータを見込み的にフェッチしてキャッシュに入れることができる。PREFETCHh 命令は、この見込み的な動作に対するヒントと見なされる。この見込み的なフェッチ動作は、命令の実行には拘束されず、任意の時点で発生する。したがって、PREFETCHh 命令は、フェンス命令(MFENCE、SFENCE、LFENCE)やロックされたメモリ参照に対して順序付けされない。
(中巻p.3-585)

ここでWB、WC、WTはそれぞれWrite Back、Write Combine、Write Throughの意(下巻p.9-5にこれらメモリタイプの一覧表がある)。

Write Combineというのはプロセッサ毎にライトバッファが6段ぐらいあって外部メモリへの複数の書き込み指令を一旦蓄積し、適当なタイミングでまとめて書き出すものらしい。ビデオメモリのように、書き込みの最終結果だけが重要(どうせ上書きされて消えるなら途中データは転送不要)、ただし最終結果確定次第すみやかに確実に書き込まれて欲しい、というタイプのデバイスへの書き込みにおいて、書き込み回数を減らす効果がある。メインメモリ相手の書き込みではWrite Combineの特性はあまりメリットがなくて、通常はWrite BackかWrite Throughかの選択になる(でいいはず)。

アトミックなアクセスサイズ

次に、アトミック性が保証されるアクセスサイズの件について

Pentium 4 プロセッサおよびP6 ファミリ・プロセッサは、これに加えて、次のメモリ操作が常にアトミックに実行されるよう保証する。
• 64 ビット境界にアライメントが調整された1 クワッドワードの読み取りまたは書き込み。(この操作はPentium プロセッサでも保証される。)
• 32 ビット・データ・バス内に納まる、キャッシュされていないメモリ位置への16 ビット・アクセス。


P6 ファミリ・プロセッサでは、これらに加えて、次のメモリ操作が常にアトミックに実行されることが保証されている。
• 32 バイト・キャッシュ・ライン内に納まる、キャッシュされたメモリ位置へのアライメントが調整されていない16、32、64 ビット・アクセス。
(下巻p.7-2)

多分64 bit境界に整列した64 bitなら問題なくアトミックに読み書きできるはず。

問題点

置換表エントリに使うには64 bitは小さすぎる。なぜなら、置換表のキーが一致したからといって目的局面の内容である保証がない(衝突しているかもしれない)ので、内容を特定するための局面ハッシュ(64 bit)をできるだけそのまま格納しておかねば現実的でないからだ。よって、最低2回の64 bitアクセスを排他的に行う必要にせまられる。

フェンス命令でロードを順序化しつつオプティミスティックロック、は得策ではない。この方式だと1エントリの読み出しにフェンス命令を都合2回発行する必要があるが、目的の置換表エントリがキャッシュでヒットする可能性は低いから、それで最低1回は外部DDRメモリに読みに行く。そして運悪く2つの64 bitワードがキャッシュライン(64 Byteらしい)の境界をまたいでいたなら、もう1回外部DDRメモリに読みに行くことになり、1エントリで64 Byteものキャッシュラインを丸々使うといった贅沢をしない限り、高い頻度で数百クロック×2回の時間ロスを生む。後述するように「128 bitを一括でキャッシュ汚染せずにロードする」方法があるからそちら一択。

解決策

64 bitの最上位ビット3 bitをスレッド番号に割り当て、当該エントリを書き込んだスレッドを特定できるようにしようと思う。ノンテンポラルなリード命令に128 bit一括で読んでこれるものがあるから、これなら1回のバーストリードかパーシャルリード(どちらかはプロセッサが適当に決めるそうな)で目的のエントリ全体(128 bit)をキャッシュスルーで一度に読んで来ることができ、レジスタ上で壊れていないか判定できる。壊れていればもう一回だけトライし、それでもダメならエントリに頼らず探索する。リード×ライト競合やライト×ライト競合があれば壊れるが、おそらく無視し得る頻度だろう。

エントリの書き込みもノンテンポラルな書き込み命令でキャッシュスルーで行いキャッシュ汚染を防ぐ。

なおノンテンポラルなリードライト命令を使う他にも、MTRRレジスタやPATを書き換えればメモリの特定領域丸ごとアンキャッシュやライトスルーにできる(そうな)のだが、それらの書き換えはカーネルモード(リング0)でないとできないからWindows上でやるにはWDMに首を突っ込まねばならない。そんな時間も知能もないから上記の方法でサクッとやろうと思う感じ、

■追記
なんか途中でアホなことを口走った気がするがいつものことなので気にしない

■追記2
特定状況でうまくいかない気がしてきたorz (スレッド011が書き込んだ場所に再度スレッド011が書き込むときのライト×リード競合)