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


序文

cudaプログラミングを使用するとプログラムのパフォーマンスが向上することをどのように確認できますか。これは通常、プログラムの実行時間を比較することで検証されます。したがって、プログラムの実行時間のタイミングに精通している場合は、最適化されたパフォーマンス効果を確認できます。

1.CUDAタイミングプログラム

Cuda は cuda イベントに基づくタイミング メソッドを提供します. 次のタイミング プログラムは cuda プログラミング ブックで紹介されています:

 // 定义变量
 cudaEvent_t start, stop;   
 // cudaEventCreate初始化变量
 CHECK(cudaEventCreate(&start));
 CHECK(cudaEventCreate(&stop));
 // 在计时的代码块之前要记录一个代表开始的事件
 CHECK(cudaEventRecord(start));
 // 需要添加cudaEventQuery操作刷新队列,才能促使前面的操作在GPU上执行
 cudaEventQuery(start); // 不用CHECK,返回值不对但不代表程序出错
 // 需要计时的代码块
 add<<<grid_size, block_size>>>(d_x, d_y, d_z, N); 
 // 在计时的代码块结束要记录一个代表结束的事件
 CHECK(cudaEventRecord(stop));
 // 要让主机等待事件stop被记录完毕
 CHECK(cudaEventSynchronize(stop));
 // 计算程序执行的时间差
 float elapsed_time;
 CHECK(cudaEventElapsedTime(&elapsed_time, start, stop));
 printf("Time = %g ms.\n", elapsed_time);
 // 销毁start和stop事件
 CHECK(cudaEventDestroy(start));
 CHECK(cudaEventDestroy(stop));

2. CUDA プログラムのタイミング

タイミングを10回繰り返し、平均値と誤差を計算します。最初は無視します。マシンが初めて CPU または GPU のウォームアップ状態にある可能性があり、測定された時間が大きすぎることがよくあります。配列を追加する前のプログラムを次のように変更します

    float t_sum = 0;
    float t2_sum = 0;
    for (int repeat = 0; repeat <= NUM_REPEATS; ++repeat)
    {
    
    
        cudaEvent_t start, stop;
        CHECK(cudaEventCreate(&start));
        CHECK(cudaEventCreate(&stop));
        CHECK(cudaEventRecord(start));
        cudaEventQuery(start);

        add<<<grid_size, block_size>>>(d_x, d_y, d_z, N);

        CHECK(cudaEventRecord(stop));
        CHECK(cudaEventSynchronize(stop));
        float elapsed_time;
        CHECK(cudaEventElapsedTime(&elapsed_time, start, stop));
        printf("Time = %g ms.\n", elapsed_time);
			 // 忽略第一次.第一次机器可能处于预热状态,测得的时间往往偏大.
        if (repeat > 0)
        {
    
    
            t_sum += elapsed_time;
            t2_sum += elapsed_time * elapsed_time;
        }

        CHECK(cudaEventDestroy(start));
        CHECK(cudaEventDestroy(stop));
    }

ここで、nvcc でコンパイルする場合、コマンド ラインに -O3 コマンドを追加する必要があります。これは最適化レベルであり、3 レベルの最適化を意味し、c++ プログラムのパフォーマンスを向上させることができます。最適化命令を使用する場合と使用しない場合の差は比較的大きく、約 2 ~ 3 倍です。

GPU アクセラレーションの鍵

上記のタイミング プログラムを使用して cuda のコア機能の時間を測定する場合、単精度と倍精度を使用して実行時間を比較すると、GeForce RTX2080Ti では、倍精度は単精度間の関係のほぼ 2 倍であることがわかります。実行時間。
倍精度および単精度の浮動小数点計算を使用するプログラムを定義します。

//  在源程序中使用条件编译,定义双精度和单精度
#ifdef USE_DP
    typedef double real; // 给double和float类型取别名,方便直接使用real,后面统一做类型的修改
    const real EPSILON = 1.0e-15;
#else
    typedef float real;
    const real EPSILON = 1.0e-6f;
#endif

コンパイル時に、コマンド ライン入力 -D USE_UP を使用して、計算に倍精度浮動小数点数を使用するオプションを指定します。

単精度を使用:
ここに画像の説明を挿入
倍精度を使用:
ここに画像の説明を挿入
上記は、計算されたカーネル関数でのプログラムの実行時間のみであり、c++ プログラムでのメモリ割り当てとデータ転送に関連する時間は計算されません。単精度を使用して、データの送信時にタイミングが開始されると、cuda プログラムの時間が大幅に増加することがわかります。
ここに画像の説明を挿入
実行時間:
ここに画像の説明を挿入
CPU のみを使用するプログラムの場合、実行時間: 次の
ここに画像の説明を挿入
情報を確認できます。

  • cuda プログラムでは、配列の追加とカーネル関数の使用は、基本的にデータ転送に多くの時間がかかるため、比較的短い時間で済みます。CPU を直接使用する場合に比べて、パフォーマンスが向上しない場合があります。
    cuda ツールボックスでは、nvprof コマンドを使用して、プログラムの各部分の実行時間の消費を表示できます。
    ここに画像の説明を挿入データ転送がメインの実行時間を占めていることを分析するのは非常に直感的であり、カーネル関数によって消費される最大時間はわずかです。 2.1864ms. 0.81% の時間。

要約すると、ホストとデバイス間のデータ転送には比較的長い時間がかかることがわかります。では、なぜ cuda のパフォーマンスを改善できると言えるのでしょうか?

  • カーネル関数の時間を比較すると、GPU には多くのコアがあり、計算速度は CPU よりもはるかに高速であることがわかります。配列を追加する操作が単純すぎて、cuda の高速化を直感的に反映できていないのでしょう。その後、より複雑な浮動小数点演算を使用することで、cuda がプログラムのパフォーマンスを大幅に改善できることが証明されました。また、データ転送には時間がかかるため、複雑な操作を一度実行し、その後はできるだけ大きなデータの転送を避ければ、大幅なパフォーマンスの向上が得られます。

ここでまとめると、GPU 高速化の鍵は次の 2 点です。
(1) データ転送の比率: 過剰なデータ転送を避けるために GPU でより多くの計算が実行され、データは PCIe 経由で転送されます。
(2) 浮動小数点計算の算術強度: 複雑な浮動小数点計算は GPU 上で実行されます。

本書では、モデルの異なる単精度GPUと倍精度GPUの実行時間の比率についても言及されており、演算強度の高い問題について、倍精度浮動小数点演算を使用した場合、TeslaシリーズGPUのパフォーマンスはGeForceシリーズGPUと比較して、より多くの利点があります。単精度の GPU はあまり多くなく、GeForce シリーズの 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/128738156