7

CUDA 3.2 と VS 2008 を使用して実装された次の行列乗算コードがあります。Windows サーバー 2008 r2 エンタープライズで実行しています。Nvidia GTX 480 を実行しています。次のコードは、約 2500 程度までの「幅」(マトリックス幅) の値で正常に動作します。

int size = Width*Width*sizeof(float);
float* Md, *Nd, *Pd;
cudaError_t err = cudaSuccess;

//Allocate Device Memory for M, N and P
err = cudaMalloc((void**)&Md, size);
err = cudaMalloc((void**)&Nd, size);
err = cudaMalloc((void**)&Pd, size);

//Copy Matrix from Host Memory to Device Memory
err = cudaMemcpy(Md, M, size, cudaMemcpyHostToDevice);
err = cudaMemcpy(Nd, N, size, cudaMemcpyHostToDevice);

//Setup the execution configuration
dim3 dimBlock(TileWidth, TileWidth, 1);
dim3 dimGrid(ceil((float)(Width)/TileWidth), ceil((float)(Width)/TileWidth), 1);

MatrixMultiplicationMultiBlock_Kernel<<<dimGrid, dimBlock>>>(Md, Nd, Pd, Width);

err = cudaMemcpy(P, Pd, size, cudaMemcpyDeviceToHost);

//Free Device Memory
cudaFree(Md);
cudaFree(Nd);
cudaFree(Pd);

「幅」を 3000 以上に設定すると、黒い画面の後に次のエラーが表示されます。 スクリーンショット

オンラインで調べたところ、カーネルが 5 秒以上ハングした後、ウォッチドッグがカーネルを強制終了していたため、一部の人々がこの問題を抱えていることがわかりました。レジストリの「TdrDelay」を編集してみましたが、これにより、黒い画面と同じエラーが表示されるまでの時間が遅れました。したがって、これは私の問題ではないと結論付けました。

コードをデバッグしたところ、次の行が原因であることがわかりました。

err = cudaMemcpy(P, Pd, size, cudaMemcpyDeviceToHost);

これは、行列乗算カーネル関数が呼び出された後にデバイスから結果セットを返すために使用するものです。この時点までのすべてが正常に実行されているようです。メモリを正しく割り当てていると思いますが、なぜこれが起こっているのかわかりません。カードに十分なメモリがなかったのではないかと思いましたが、cudaMalloc がエラーを返すべきではないでしょうか? (デバッグ中にそうでないことを確認しました)。

どんなアイデア/支援も大歓迎です!...どうもありがとうございました!!

カーネルコード:

//Matrix Multiplication Kernel - Multi-Block Implementation
__global__ void MatrixMultiplicationMultiBlock_Kernel (float* Md, float* Nd, float* Pd, int Width) 
{
int TileWidth = blockDim.x;

//Get row and column from block and thread ids
int Row = (TileWidth*blockIdx.y) + threadIdx.y;
int Column = (TileWidth*blockIdx.x) + threadIdx.x;

//Pvalue store the Pd element that is computed by the thread
float Pvalue = 0;

for (int i = 0; i < Width; ++i)
{
    float Mdelement = Md[Row * Width + i];
    float Ndelement = Nd[i * Width + Column];
    Pvalue += Mdelement * Ndelement;
}

//Write the matrix to device memory each thread writes one element
Pd[Row * Width + Column] = Pvalue;
}

共有メモリを使用するこの他の関数もあり、同じエラーが発生します。

電話:

            MatrixMultiplicationSharedMemory_Kernel<<<dimGrid, dimBlock, sizeof(float)*TileWidth*TileWidth*2>>>(Md, Nd, Pd, Width);

カーネルコード:

 //Matrix Multiplication Kernel - Shared Memory Implementation
 __global__ void MatrixMultiplicationSharedMemory_Kernel (float* Md, float* Nd, float* Pd, int Width) 
 {
int TileWidth = blockDim.x;

//Initialize shared memory
extern __shared__ float sharedArrays[];
float* Mds = (float*) &sharedArrays;
float* Nds = (float*) &Mds[TileWidth*TileWidth];

int tx = threadIdx.x;
int ty = threadIdx.y;

//Get row and column from block and thread ids
int Row = (TileWidth*blockIdx.y) + ty;
int Column = (TileWidth*blockIdx.x) + tx;
float Pvalue = 0;

//For each tile, load the element into shared memory
for( int i = 0; i < ceil((float)Width/TileWidth); ++i)
{
    Mds[ty*TileWidth+tx] = Md[Row*Width + (i*TileWidth + tx)];
    Nds[ty*TileWidth+tx] = Nd[(ty + (i * TileWidth))*Width + Column]; 

    __syncthreads();

    for( int j = 0; j < TileWidth; ++j)
    {
        Pvalue += Mds[ty*TileWidth+j] * Nds[j*TileWidth+tx];
    }

    __syncthreads();
}

//Write the matrix to device memory each thread writes one element
Pd[Row * Width + Column] = Pvalue;
}
4

3 に答える 3

10

WDDM タイムアウトの制御

問題は、実際にはカーネルではなく、cudaMemcpy(). カーネルを起動すると、GPU はオフになり、CPU と非同期で作業を行います。そのため、作業が完了するのを待たなければならないのは、GPU と同期するときだけです。cudaMemcpy()暗黙の同期が含まれているため、ここで問題が発生します。

カーネルの後に呼び出してこれを再確認すると、問題はではなく にあるcudaThreadSynchronize()ように見えます。cudaThreadSynchronize()cudaMemcpy()

TDR タイムアウトを変更した後、マシンを再起動しましたか? 残念ながら、TDR 設定を変更するには、Windows を再起動する必要があります。この Microsoft ドキュメントには、利用可能なすべての設定がかなり適切に説明されています。

カーネルの問題

この場合、問題は実際には WDDM タイムアウトではありません。i解決する必要があるエラーがカーネルにあり (たとえば、反復ごとに複数のインクリメントができるはずです) matrixMul、SDK のサンプルをチェックアウトすると役立つ場合があります。ちなみに、実際には (パフォーマンスのために) CUBLAS を使用して行列の乗算を実行した方がよいので、これが学習演習であることを願っています。

コードの最も重大な問題は、共有メモリを実際に割り当てずに使用していることです。カーネルには次のものがあります。

//Initialize shared memory
extern __shared__ float sharedArrays[];

ただし、カーネルを起動するときに、各ブロックに割り当てる共有メモリの量を指定しません。

MatrixMultiplicationMultiBlock_Kernel<<<dimGrid, dimBlock>>>(Md, Nd, Pd, Width);

<<<>>> 構文は実際には 4 つの引数を取り、3 番目と 4 番目はオプションです。4 番目は、計算とデータ転送の間のオーバーラップを取得するために (およびカーネルの同時実行のために) 使用されるストリーム インデックスですが、3 番目の引数はブロックごとの共有メモリの量を指定します。この場合TileWidth * TileWidth、float を共有メモリに格納する必要があると想定しているため、次のように使用します。

MatrixMultiplicationMultiBlock_Kernel<<<dimGrid, dimBlock, dimBlock.x * dimBlock.x * sizeof(float)>>>(Md, Nd, Pd, Width);

主な問題

コメントで言及したように、実際の問題は、マトリックスの幅がブロック幅の倍数ではないことでした(正方形であるため、高さは、端を超えるスレッドが配列の端を超えてアクセスすることを意味します。コードは次のいずれかである必要があります。複数でないケースを処理するか、幅がブロックサイズの倍数であることを確認する必要があります。

これは以前に提案すべきだったのですが、cuda-memcheckこのようなメモリ アクセス違反をチェックするために実行すると便利なことがよくあります。

于 2010-10-30T18:38:36.923 に答える
1

ドライバーのタイムアウト設定を変更する必要があります。これは、ドライバーの障害によりシステムが応答しなくなるのを防ぐための Windows の機能です。その方法を説明しているMicrosoft ページを確認してください。

于 2010-10-30T20:00:06.410 に答える
0

GPU デバイスの「タイムアウト」フラグの設定も確認する必要があります。CUDA SDK がインストールされている場合、「deviceQuery」アプリがこのプロパティを報告すると思います。

于 2010-11-01T08:08:47.977 に答える