CUDAの質問に対する回答やコメント、そしてCUDA タグ ウィキ、すべての API 呼び出しの戻りステータスでエラーをチェックすることがよく提案されています。API ドキュメントには、、、などの関数が含まれていますcudaGetLastError
がcudaPeekAtLastError
、cudaGetErrorString
余分なコードを必要とせずにエラーを確実にキャッチして報告するには、これらを組み合わせる最適な方法は何でしょうか。
ベストアンサー1
おそらく、ランタイム API コードのエラーをチェックする最良の方法は、次のようにアサート スタイルのハンドラー関数とラッパー マクロを定義することです。
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, const char *file, int line, bool abort=true)
{
if (code != cudaSuccess)
{
fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
if (abort) exit(code);
}
}
次に、各 API 呼び出しをマクロでラップしますgpuErrchk
。このマクロは、ラップした API 呼び出しの戻りステータスを処理します。次に例を示します。
gpuErrchk( cudaMalloc(&a_d, size*sizeof(int)) );
呼び出しでエラーが発生した場合、エラーの内容と、エラーが発生したコード内のファイルと行を示すテキスト メッセージが出力されstderr
、アプリケーションが終了します。必要に応じて、より高度なアプリケーションでgpuAssert
呼び出しを行うのではなく、例外を発生させるように変更することも可能です。exit()
2 つ目の関連する質問は、標準のランタイム API 呼び出しのようにマクロ呼び出しで直接ラップできないカーネル起動時のエラーをどのようにチェックするかです。カーネルの場合は次のようになります。
kernel<<<1,1>>>(a);
gpuErrchk( cudaPeekAtLastError() );
gpuErrchk( cudaDeviceSynchronize() );
まず無効な起動引数をチェックし、次にカーネルが停止して実行エラーをチェックするまでホストを待機させます。次のような後続のブロッキング API 呼び出しがあれば、同期を省略できます。
kernel<<<1,1>>>(a_d);
gpuErrchk( cudaPeekAtLastError() );
gpuErrchk( cudaMemcpy(a_h, a_d, size * sizeof(int), cudaMemcpyDeviceToHost) );
この場合、cudaMemcpy
呼び出しはカーネル実行中に発生したエラーか、メモリコピー自体からのエラーのいずれかを返すことができます。これは初心者にとっては混乱を招く可能性があるため、問題が発生している場所を理解しやすくするために、デバッグ中にカーネルを起動した後、明示的な同期を使用することをお勧めします。
使用する際は注意してくださいCUDA 動的並列処理非常によく似た方法論は、デバイス カーネル内の CUDA ランタイム API の使用や、デバイス カーネルの起動後にも適用できます。また、適用する必要があります。
#include <assert.h>
#define cdpErrchk(ans) { cdpAssert((ans), __FILE__, __LINE__); }
__device__ void cdpAssert(cudaError_t code, const char *file, int line, bool abort=true)
{
if (code != cudaSuccess)
{
printf("GPU kernel assert: %s %s %d\n", cudaGetErrorString(code), file, line);
if (abort) assert(0);
}
}
CUDA Fortranのエラーチェックも同様です。ここそしてここ一般的な関数エラー戻り構文。CUDA C++ に似た方法を使用して、カーネルの起動に関連するエラーを収集します。