特に次の点に関して、CUDAで共有メモリとグローバルメモリを使用する方法について混乱しています。
cudaMalloc()
を使用する場合、共有メモリまたはグローバルメモリへのポインタを取得しますか?変数を共有メモリに格納することは、カーネル経由でアドレスを渡すことと同じですか?つまり持つ代わりに
__global__ void kernel() {
__shared__ int i;
foo(i);
}
なぜ同等にしない
__global__ void kernel(int *i_ptr) {
foo(*i_ptr);
}
int main() {
int *i_ptr;
cudaMalloc(&i_ptr, sizeof(int));
kernel<<<blocks,threads>>>(i_ptr);
}
グローバルメモリと共有メモリの特定の速度の問題について多くの質問がありましたが、実際にどちらを使用するかについての概要を網羅したものはありません。
どうもありがとう
CudaMalloc()を使用する場合
ホストに通信して戻すことができるデータをgpuに保存するには、解放されるまで存続するメモリを割り当てる必要があります。アプリケーションが閉じるか解放されるまで存続するヒープスペースとしてグローバルメモリを表示する必要があります。そのメモリ領域へのポインタを持つスレッドとブロックに。共有メモリは、カーネルのブロックが終了するまで存続するスタックスペースと見なすことができ、可視性は同じブロック内のスレッドのみに制限されます。したがってcudaMallocは、グローバルメモリにスペースを割り当てるために使用されます。
共有メモリまたはグローバルメモリへのポインタを取得しますか?
グローバルメモリにあるメモリアドレスへのポインタを取得します。
グローバルメモリはホストまたはデバイスに存在しますか?
グローバルメモリはデバイスに常駐します。ただし、マップされたメモリを使用してホストメモリを「グローバル」メモリとして使用する方法があります。以下を参照してください。 CUDAゼロコピーメモリの考慮事項 ただし、バス転送速度の制限により速度が遅くなる場合があります。
どちらかにサイズ制限はありますか?
グローバルメモリのサイズは、カードごとに異なり、なしから32GB(V100)まであります。共有メモリは計算能力に依存しますが。計算機能2.x未満のものは、マルチプロセッサーごとに最大16KBの共有メモリーを備えています(マルチプロセッサーの量はカードごとに異なります)。また、計算機能が2.x以上のカードは、マルチプロセッサあたり最低48KBの共有メモリを搭載しています。
参照 https://en.wikipedia.org/wiki/CUDA#Version_features_and_specifications
マップメモリを使用している場合、唯一の制限は、ホストマシンのメモリ容量です。
どちらにアクセスする方が速いですか?
生の数値に関しては、共有メモリの方がはるかに高速です(共有メモリ〜1.7TB /秒、グローバルメモリ〜XXXGB /秒)。ただし、共有メモリを何かで満たす必要があることを行うには、通常、グローバルメモリからプルします。グローバルメモリへのメモリアクセスが結合され(ランダムではなく)、大きなWordサイズである場合、カードとそのメモリインターフェイスによっては、理論上の制限である数百GB/sに近い速度を実現できます。
共有メモリの使用は、スレッドのブロック内で、グローバルメモリから既にプルまたは評価されたデータを再利用する必要がある場合です。したがって、再度グローバルメモリからプルするのではなく、同じブロック内の他のスレッドが共有メモリに配置して、確認して再利用します。
また、同時に実行できるワークグループの数に影響を与えるレジスターのプレッシャーを減らすために、スクラッチパッドとして使用することも一般的です。
変数を共有メモリに保存することは、カーネル経由でアドレスを渡すことと同じですか?
いいえ、何かのアドレスを渡した場合、それは常にグローバルメモリへのアドレスです。カーネルが共有メモリをその定数に設定する定数として渡すか、必要なときにカーネルによってプルされるグローバルメモリにアドレスを渡す場合を除いて、ホストから共有メモリを設定することはできません。
グローバルメモリの内容は、グリッドのすべてのスレッドに表示されます。どのスレッドでも、グローバルメモリの任意の場所で読み書きできます。
共有メモリは、グリッドのブロックごとに分かれています。ブロックのどのスレッドも、そのブロックの共有メモリに対して読み書きできます。あるブロックのスレッドは、別のブロックの共有メモリにアクセスできません。
cudaMalloc
は常にグローバルメモリを割り当てます。16 KB/Block
、compute 2.0以降には48 KB/Block
デフォルトでは共有メモリ。更新:
コンピューティング機能7.0(Voltaアーキテクチャ)のデバイスでは、次の条件を満たす場合に、ブロックごとに最大96 KBの共有メモリを割り当てることができます。
cudaFuncSetAttribute
を使用して指定されます。__global__ void MyKernel(...)
{
extern __shared__ float shMem[];
}
int bytes = 98304; //96 KB
cudaFuncSetAttribute(MyKernel, cudaFuncAttributeMaxDynamicSharedMemorySize, bytes);
MyKernel<<<gridSize, blockSize, bytes>>>(...);
CUDA共有メモリは、ブロック内のスレッド間で共有されるメモリです。つまり、グリッド内のブロック間で、共有メモリの内容は定義されていません。手動で管理するL2キャッシュと考えることができます。
通常、グローバルメモリはデバイスに常駐しますが、CUDAの最近のバージョン(デバイスがサポートしている場合)は、ホストメモリをデバイスのアドレス空間にマップし、in-situ DMAホストからデバイスメモリへの転送をトリガーします。そのような機会に。
デバイスによっては、共有メモリにサイズ制限があります。 CUDAデバイスを列挙するときに取得されるデバイス機能で報告されます。グローバルメモリは、GPUで利用可能なメモリの合計によって制限されます。たとえば、GTX680は48kiBの共有メモリと2GiBデバイスメモリを提供します。
共有メモリは、グローバルメモリよりもアクセスが高速ですが、効率を上げるには、アクセスパターンを(共有メモリとグローバルメモリの両方について)注意深く調整する必要があります。アクセスパターンを適切に調整できない場合は、テクスチャを使用します(これもグローバルメモリですが、調整されていないアクセスに対処できるように、異なる循環とキャッシュを通じてアクセスします)。
変数を共有メモリに保存することは、カーネル経由でアドレスを渡すことと同じですか?
いいえ、違います。あなたが提案したコードは、in-situ転送されたグローバルメモリを使用する場合です。共有ブロックの内容はスレッドの実行ブロック内でのみ定義されるため、カーネル間で共有メモリを渡すことはできません。