• トップ
  • G-DEPについて
  • ご購入ガイド
  • サポート
  • お問い合わせ

G-DEPトップ  >  G-DEPの高速演算記  >  高速演算記 第2回 「Fermi解説その2-L1/L2キャッシュは効いている? ~メモリアーキテクチャの改良~」

高速演算記 第2回 「Fermi解説その2-L1/L2キャッシュは効いている? ~メモリアーキテクチャの改良~」

前回コラムではFermiに採用された新メモリアーキテクチャについて紹介しました。Fermiは新しくL1キャッシュとL2キャッシュを搭載し、グローバルメモリアクセスが改善されています。そしてCUDA Toolkit 3.1に含まれているPTX ISA 2.1のマニュアルには、このキャッシュに関連する記載が新たに加わっています。

今回は新たに加わった命令をもとに、L1, L2キャッシュについてレポートしようと思います。

キャッ シュとは
CPUが高速になると、CPUチップの外に存在するメモリへのアクセスが相対的に遅くなってきます。メモリの高速化はCPUに比べ技術的進歩が難しいためです。
CPU内にメモリを搭載すれば、高速にアクセスできます。頻繁に使用するデータを内部メモリに一時的に取り込むことで高速化を図ることが可能です。
これがキャッシュ(Cache)と呼ばれるメモリですが、有限のチップ面積をCPU機能とメモリ、どちらに割り当てるかはトレードオフになります。
さてキャッシュにデータを取り込む時は通常、キャッシュラインと呼ばれるひと単位として扱います。Fermiの場合128バイトを一括してメモリへ読み書きしています。

まずはFermiハードウェアアーキテクチャについてのおさらいをしましょう。
Fermiでは数百個の演算コアが搭載されています。各演算コアは32コアを単位とした実行ユニットから構成されています。TeslaC2050では これらの実行ユニットが14個搭載されており、GPUで合計448コア搭載している計算になります。

FermiはGPU全体で共有されるL2キャッシュと、各実行ユニットに個別のL1キャッシュが搭載されました。そしてL1キャッシュはプログラム構成によって16Kbあ るいは48Kb選択可能となっています。

出展: NVIDIA Fermi White Paper
Fermi Architecture

L1, L2各キャッシュとも128バイトのキャッシュラインから構成されています[1]。

ラ イトバックとコヒーレンス

キャッシュに取り込まれたメモリの値を更新する時、直接外部メモリに書き出さず、キャッシュの値のみ更新することがライトバックと呼ばれています。
マルチコアCPUにおいて、各CPU個別にキャッシュがある場合、一つのCPUがメモリの値を更新した時、他のCPUでも個別にキャッシュで値が保持され ている場合、それらも新しい値に更新し、整合性をとる必要があります。整合性がとれる状態がコヒーレンシと呼ばれています。
Fermiでは、各実行ユニット間でキャッシュの整合性が保たれるよう、CUDAドライバーが適宜L1キャッシラインを破棄しているようです。

Cache Coherency

さてこのキャッシュですが、共有L2キャッシュはコヒーレントですが個別に 存在するL1キャッシュ間でコヒーレンシは保たれていません。
書き込みに関してもグローバルメモリへ値を書き出す場合、L2キャッシュはライトバックが採用されています。しかしL1に存在するキャッシュラインはデー タの保存先がスレッドに固有のローカルメモリであればライトバックされますが、グローバルメモリであれば破棄されL1に保持されません。


PTX ISA 2.1マニュアルをみるとロード、ストア命令にキャッシュに関する新たな設定が加わりました。また、キャッシュラインをプリフェッチするための命令も追加されています[1]。メモリロード時以下の選択が可能です。

  • L1とL2両方でキャッシュする
  • L2のみにデータをキャッシュする
  • キャッシュを無視してデータを直接ロー ドする
  • データ読み込み後そのキャッシュラインが優勢的に破棄されるよう設定

などがあります。最後の設定は、ローカルメモリにあふれたレジスタを取り出したり、スタックフレーム復帰等、読み込み回数が限定される操作向けの設定です。また、読み込み回数が少ないストリーム型のデータにも適した設定となっています。

通常はコンパイラがコードに最適化を施してくれるのであまり気にすることがない部分ですが、より詳細に動作を制御する場合はこれら命令を利用してみてはいかがでしょうか。

添付の図は、L1キャッシュの効果を測定した結果です。テストとしてメモリ領域を確保し、一定のオフセットごとにメモリをアクセスするプログラムの実行時間を計測します。実際のコードは簡単なポインタ追跡プログラムで[3]などでも同様の手法が使用されています。

下記図より、L1キャッシュは16kb 16-wayキャッシュではないかと推測できます。また、L1キャッシュ内のデータは十数サイクルでアクセス可能と判断できます。L2キャッシュは百数十サイクルのアクセスタイムが必要と判断できます。

Fermi L1 Cache access latency

参考までにL1キャッシュを48kbとして構成した場合のアクセスタイムも掲載しました。メモリアクセスは16kbの構成と同程度ですが、48kbのワー クデータまで完全にキャッシュに収まっているのが伺えます。

キャッ シュライン格納方法

キャッシュラインをキャッシュのどこに格納するか、いくつか方法があります。Fully associativeと呼ばれる方法は、外部メモリから取ってきたキャッシュラインをキャッシュ内部のどこにでも格納できるとした方法です。この方法で はCPUが外部メモリを参照する時、キャッシュ内にコピーが存在するか検索するのが大変になります。
通常はn-way set associativeと呼ばれる方法が採用されます。これは、キャッシュをn個の領域(way)に分割し、外部メモリをキャッシュ内に格納できる場所を n個に限定する方法です。各wayで格納する場所はメモリ番地から算出されます。
図では各wayは外部メモリ3箇所の何れかのアドレスにある値を保持できます。2-wayですので、2つまで保持できますが、3つ目の値を参照するには何 れかのwayにある値を破棄する必要があります。

set associativity

Fermi L1 Cache access latency (48kb)
最後にL1キャッシュをオフにしてL2キャッシュのアクセスを測定してみました。
Fermi L2 Cache access latency
図を見ますと、640kbのワークセットはアクセスタイムが同一となっていますが、768kbのワークセットのアクセスタイムが均一ではないことが伺えます。この要因に関して現時点では確定できていませんが、計測ミスの可能性も考慮しつつ、下記は推測となりますが、

  • Fermiカーネルが使用できるL2キャッシュは640kbとなっている。
  • CUDAドライバーが一部キャッシュを使用しているためカーネルが完全に利用することができない。

といったことが考えられます。これについては情報がありましたらまた何か報告できればと思います。

いずれにしましても、L1キャッシュ、L2キャッシュが搭載されたことで外部メモリへのアクセス速度が改善されていることが伺えます。キャッシュを有効利用することでCUDAプログラムがさらに高速動作することが期待できます。

さて、次回もFermiで強化された機能について報告していきたいとおもいます。

[1] NVIDIA, PTX : Parallel Thread Execution ISA Version 2.1, Ch. 8.7.5
[2] NVIDIA, NVIDIA CUDA C Programming Guide, G.4.2
[3] V. Volkov, J.W. Demmel, Benchmarking GPUs to Tune Dense Linear Algebra, ACM/IEEE Conference on Supercomputing, 2008

(G-DEP CUDAエンジニア 田原哲雄)