CUDA プログラミングに関する注意事項 (5)


序文

cuda のメモリ構成は、GPU を使用する際のパフォーマンスを最大化します。また、デバイスのメモリを合理的に使用することも非常に重要です。

CUDA メモリ編成

表に示すように:

メモリータイプ 物理的な位置 アクセス許可 可視範囲 ライフサイクル
グローバルメモリ オフチップ 読み書き可能 すべてのスレッドとホスト側 ホストによって割り当ておよび解放される
一定の記憶 オフチップ 読み取り専用 すべてのスレッドとホスト側 ホストによって割り当ておよび解放される
テクスチャと表面の記憶 オフチップ 通常は読み取り専用 すべてのスレッドとホスト側 ホストによって割り当ておよび解放される
登録メモリ チップ内 読み書き可能 シングルスレッド どこのスレッド
ローカルメモリ オフチップ 読み書き可能 シングルスレッド どこのスレッド
共有メモリ チップ内 読み書き可能 シングルスレッドブロック スレッドブロック

グローバルメモリ

定義: ここでのグローバル メモリとは、カーネル関数内のすべてのスレッドがデータにアクセスできるメモリを指します。
役割: カーネル関数によって提供されるデータを保存し、ホストとデバイスの間、およびデバイスとデバイスの間でデータを転送します。
GPU チップ上ではないため、カーネル関数にデータを供給するときのレイテンシが高くなり、アクセス速度が遅くなります。
メモリ容量は基本的にGPUのビデオメモリと同じです。
読み書き可能です。
動的グローバルメモリ変数: 先ほどのcuda配列追加プログラムで定義したd_x, d_y, d_zを動的に確保. まずcudaMalloc()でデバイスメモリを確保し, cudaMemcpy()でホスト上のデータをデバイスに転送.割り当てられたメモリにアクセスし、カーネル関数でその値を変更します。
静的グローバル メモリ変数: ホストとデバイス間のデータ転送には cudaMemcpyToSymbol() を使用し、デバイスとホスト間のデータ転送には cudaMemcpyFromSymbol() を使用します。カーネル関数では、静的グローバル メモリ変数に直接アクセスでき、パラメーターとしてカーネル関数に渡す必要はありません。
によって関数の外で定義される

__device__ T x;  // 单个变量
__device__ T y[N];	// 固定长度的数组

例:
ここに画像の説明を挿入

一定の記憶

定義: 一定のキャッシュを持つグローバル メモリであり、その数は 64kb のみに制限されています。
機能: グローバルメモリと同じ。
読み取りのみ可能で書き込みはできず、キャッシュがあるため、コンスタント メモリのアクセス速度はグローバル メモリより高速です。
用途: cuda 配列加算プログラムの const int N は、定数メモリを使用する変数です。

テクスチャ メモリとサーフェス メモリ

定義: 定数メモリと同様に、
一般に読み取りのみ可能で、サーフェス メモリも書き込み可能です。コンピューティング能力が 3.5 以上の GPU の場合、__ldg() 関数を使用して読み取り専用データ キャッシュを介して読み取り専用グローバル メモリ データを読み取ると、テクスチャ メモリを使用した高速化効果が得られるだけでなく、コードが簡潔になります。 .

登録

定義: カーネル関数で修飾子を持たない変数は、通常、レジスタ (おそらくローカル メモリ) に格納されます。
レジスタは読み書きできます。レジスタメモリはオンチップで全メモリ中最速のアクセス速度を持っていますが、その数には限りがあります。
cuda 配列加算プログラムで int n = blockDim.x * blockIdx.x + threadIdx.x を使用します;
n はレジスタ変数です。カーネル関数で z[n] = x[n] + y[n] を使用し、変数 n を登録し、割り当て番号の右側で計算された値をそれに割り当てます。
ライフ サイクルは、定義された時点からスレッドの終了まで、所有するスレッドのライフ サイクルと一致します。A register variable is only visible to one thread. レジスタ変数の名前は異なるスレッドで同じですが、変数の値は異なります。これは、別のコピーを作成することと同じです。

ローカルメモリ

定義: レジスターとほぼ同じ。
レジスタに収まらない変数はローカルメモリに配置される場合があり、この判断はコンパイラによって自動的に行われます。

共有メモリ

定義: レジスタに似ていますが、共有メモリはスレッド ブロック全体から見えます。
機能: グローバル メモリへのアクセスを減らすか、グローバル メモリへのアクセス モードを改善します。
そのライフ サイクルは、スレッド ブロック全体と一致しています。
使用法: 変数をカーネル関数で共有メモリ変数として定義するには、定義ステートメントに修飾子 __shared__ を追加します。

__shared__ real s_y[128];

L1 および L2 キャッシュ

Fermi アーキテクチャから始まり、SM レベルの L1 キャッシュ (第 1 レベルのキャッシュ) とデバイスレベルの L2 キャッシュ (第 2 レベルのキャッシュ) があります。
これは主に、グローバル メモリとローカル メモリへのアクセスをキャッシュしてレイテンシを短縮するために使用されます。L1 および L2 キャッシュは、プログラム可能なキャッシュではありません (ユーザーがコンパイラにいくつかの選択を行うように指示することはできません)。

SMの構成

(1) 一定数のレジスタ
(2) 一定量の共有メモリ (
3) 定数メモリのキャッシュ
(4) テクスチャおよびサーフェス メモリのキャッシュ
(5) L1 キャッシュ
(6) 異なる 2 つのワープ スケジューラスレッド コンテキストを切り替え、準備完了ワープの実行命令を発行します。
(7) 実行コア: 整数演算のいくつかのコア、単精度浮動小数点演算のいくつかのコア、倍精度浮動小数点演算のいくつかのコア、単精度浮動小数点超越関数のいくつかの特殊関数ユニット、およびいくつかのコア混合精度コアのテンソル。

API 関数クエリ デバイス

いくつかの cuda api プログラムを使用して、デバイスのいくつかの仕様を照会します。

#include "error.cuh"
#include <stdio.h>

int main(int argc, char *argv[])
{
    
    
    // 设置查询的设备编号.
    int device_id = 0; 
    if (argc > 1) device_id = atoi(argv[1]);
    // cudaSetDevice()函数将对所指定的设备进行初始化
    CHECK(cudaSetDevice(device_id));
	// 定义设备输出规格的一些结构体变量
    cudaDeviceProp prop;
    CHECK(cudaGetDeviceProperties(&prop, device_id));  // 得到了device_id设备的性质,存放在结构体变量中的prop中.

    printf("Device id:                                 %d\n", 
        device_id);
    printf("Device name:                               %s\n",
        prop.name);
    printf("Compute capability:                        %d.%d\n",
        prop.major, prop.minor);
    printf("Amount of global memory:                   %g GB\n",
        prop.totalGlobalMem / (1024.0 * 1024 * 1024));
    printf("Amount of constant memory:                 %g KB\n",
        prop.totalConstMem  / 1024.0);
    printf("Maximum grid size:                         %d %d %d\n",
        prop.maxGridSize[0], 
        prop.maxGridSize[1], prop.maxGridSize[2]);
    printf("Maximum block size:                        %d %d %d\n",
        prop.maxThreadsDim[0], prop.maxThreadsDim[1], 
        prop.maxThreadsDim[2]);
    printf("Number of SMs:                             %d\n",
        prop.multiProcessorCount);
    printf("Maximum amount of shared memory per block: %g KB\n",
        prop.sharedMemPerBlock / 1024.0);
    printf("Maximum amount of shared memory per SM:    %g KB\n",
        prop.sharedMemPerMultiprocessor / 1024.0);
    printf("Maximum number of registers per block:     %d K\n",
        prop.regsPerBlock / 1024);
    printf("Maximum number of registers per SM:        %d K\n",
        prop.regsPerMultiprocessor / 1024);
    printf("Maximum number of threads per block:       %d\n",
        prop.maxThreadsPerBlock);
    printf("Maximum number of threads per SM:          %d\n",
        prop.maxThreadsPerMultiProcessor);

    return 0;
}

デバイス設定のクエリ:
ここに画像の説明を挿入
これらの出力から、GPU のメモリ構成と各メモリの最大容量を確認できます。

要約する

cuda プログラム実行のタイミング方法と GPU パフォーマンス アクセラレーションの分析
参考:
ブログの内容が侵害されている場合は、連絡してすぐに削除できます。
CUDA プログラミング: 基本と実践
https://docs.nvidia.com/cuda/
https://docs.nvidia.com/cuda/cuda-runtime-api
https://github.com/brucefan1983/CUDA-Programming

おすすめ

転載: blog.csdn.net/weixin_41311686/article/details/128743188
おすすめ