CUDAのメモリの種類が多くて面倒だという話
はじめに
この記事はKMCアドベントカレンダー2013の5日目の記事です。
tyage氏の書いた昨日の記事はこちらです。
ちなみにKMCにはガチ情報系な人が多く居ますが、意外とCUDA書いてる人って全然居ないんですよ。
私も普段ブログを書いてはいるのですが、あまりにも技術的な話と程遠い日常的な愚痴ばかり書いているブログなのでこのイベントに参加させるのは場違いなのでは、というのがこのブログを作るきっかけだったりします。
ついでにこのブログが大学の研究室の人に知れても大丈夫なようにわざわざアカウント分けたりとかも、ね。
さてさて、今回はCUDAのメモリ管理がめんどいというお話を今回はしていきましょう。
デバイスから見られるメモリの種類
デバイス(GPU)から扱えるメモリにはいくつかの種類があります。
それぞれどういった特徴を持つか見てみましょう。
グローバルメモリ
ホスト(CPU)側から書き込みができるメモリです。
容量も非常に大きい(数GBとか)です。
その分読み書きには手間がかかります。
シェアードメモリ
ブロックごとに積まれているメモリです。
容量が小さい、ブロックごとにしか共有できない、カーネルから出ると使えない(同じIDでも同じブロックになるとは限らない)と厄介な性質を持ちますが読み書きは速いです。
レジスタ
スレッドごとに用いるメモリです。
非常に読み書きは速いですが非常に小さく、スレッド間でレジスタのデータをやりとりする場合には昨日触れたWarp Shuffleやシェアードメモリの使用が必要になります。
キャッシュ
通常のキャッシュの他にコンスタントキャッシュ、テクスチャキャッシュが有ります。
いずれも明示的に用いることは出来ませんが後半の特殊なものについてはある程度意図的に用いることになります。
ここまでが実際に存在するメモリです。
さらにこれに加えてコード上から仮想的に指定するメモリが存在します。
これらは実際はグローバルメモリ上にありますがコンスタントキャッシュ、テクスチャキャッシュを活用して高速で読み込みを行うことが出来ます。
テクスチャメモリ
テクスチャキャッシュを用いるためのメモリです。
1~3次元の格子状にデータを配置してそのインデックスを用いて読み込みを行います。
1度読み込みを行うとその周辺の値をまとめてテクスチャキャッシュに読み込み、そこから読み込みができるようになります。
そのため、近接する場所の値を連続して読み込むのは高速になります。
ただし、キャッシュに読み込んだデータを返せないため書き込みはできません。
コンスタントメモリ
コンスタントキャッシュを用いるためのメモリです。
ホストからは書き込めますがデバイスからは読み込みしか出来ません。
コンスタントメモリの中身は全てコンスタントキャッシュに移すため、使える容量は小さいです。
サーフェスメモリ
テクスチャキャッシュを使いつつもデバイス側から書き込みができるメモリ……らしいのですが使っている例が全く見つからずよくわかりません。
いずれ自分で調べてここに書きたいです。
と、まぁ沢山の種類があるわけです。
CUDAで効率化を図る場合はこれらを適切に使い分ける必要があるわけです。
どう使い分ける?
グローバルメモリの使用を極力避けるのが肝になります。
何度も読み込むデータはシェアードメモリに移しましょう。
シェアードメモリは明示的に扱えるキャッシュのようなものとしてガンガン使いましょう。
定数はコンスタントメモリに入れましょう。これはシンプルにできます。
テクスチャメモリは使いどころが限られますが、1~3次元上に並んだデータに連続アクセスする場合には大きな効果を発揮します。
無理して使うほどではないですが、もともとそういう形のデータを扱うなら使わない手はないでしょう。
最後に
KMCアドベントカレンダー2013、明日はohai氏による「Rubyでバイナリデータを取り扱う方法について」らしいですよ。
他にもいろいろあるので興味があるテーマについて話しそうな日があったら見てみて下さいね。