5

しばらくの間cudaを学び始めましたが、次の問題があります

以下で私がどのようにやっているかを見てください:

GPUをコピーする

int* B;
// ...
int *dev_B;    
//initialize B=0

cudaMalloc((void**)&dev_B, Nel*Nface*sizeof(int));
cudaMemcpy(dev_B, B, Nel*Nface*sizeof(int),cudaMemcpyHostToDevice);
//...

//Execute on GPU the following function which is supposed to fill in 
//the dev_B matrix with integers


findNeiborElem <<< Nblocks, Nthreads >>>(dev_B, dev_MSH, dev_Nel, dev_Npel, dev_Nface, dev_FC);

CPUを再度コピーします

cudaMemcpy(B, dev_B, Nel*Nface*sizeof(int),cudaMemcpyDeviceToHost);
  1. 配列Bをdev_Bにコピーするのにかかる時間は、ほんの一瞬です。ただし、配列dev_BをBにコピーするのは永遠にかかります。
  2. findNeiborElem関数には、各スレッドのループが含まれます。たとえば、次のようになります。

    __ global __ void findNeiborElem(int *dev_B, int *dev_MSH, int *dev_Nel, int *dev_Npel, int *dev_Nface, int *dev_FC){
    
        int tid=threadIdx.x + blockIdx.x * blockDim.x;
        while (tid<dev_Nel[0]){
            for (int j=1;j<=Nel;j++){
                 // do some calculations
                 B[ind(tid,1,Nel)]=j// j in most cases do no go all the way to the Nel reach
                 break; 
            }
        tid += blockDim.x * gridDim.x; 
        }
    }
    

それについて非常に奇妙なのは、dev_BをBにコピーする時間は、jインデックスの反復回数に比例するということです。

たとえば、その場合Nel=5、時間は約5 secです。

増やすとNel=20時間は約20 secです。

コピー時間は、Matrixの値を割り当てるために必要な内部反復とは無関係である必要がありますdev_B

また、同じマトリックスをCPUとの間でコピーする時間も同じオーダーになると思います。

何が悪いのか分かりますか?

4

2 に答える 2

3

clock()を使用して時間を測定する代わりに、イベントを使用する必要があります。

イベントを使用すると、次のようなものになります。

  cudaEvent_t start, stop;   // variables that holds 2 events 
  float time;                // Variable that will hold the time
  cudaEventCreate(&start);   // creating the event 1
  cudaEventCreate(&stop);    // creating the event 2
  cudaEventRecord(start, 0); // start measuring  the time

  // What you want to measure
  cudaMalloc((void**)&dev_B, Nel*Nface*sizeof(int));
  cudaMemcpy(dev_B, B, Nel*Nface*sizeof(int),cudaMemcpyHostToDevice);

  cudaEventRecord(stop, 0);                  // Stop time measuring
  cudaEventSynchronize(stop);               // Wait until the completion of all device 
                                            // work preceding the most recent call to cudaEventRecord()

  cudaEventElapsedTime(&time, start, stop); // Saving the time measured

編集:追加情報:

「カーネルの起動は、終了する前にCPUスレッドに制御を戻します。したがって、タイミング構造は、カーネルの実行時間と2番目のmemcpyの両方を測定します。カーネルの後にコピーのタイミングをとる場合、タイマーコードはすぐに実行されますが、 cudaMemcpyは、カーネルが完了するのを待ってから開始します。これは、データリターンのタイミング測定がカーネルループの反復に基づいて変化するように見える理由も説明します。また、カーネル関数に費やされる時間が「無視できる」理由も説明します。RobertCrovellaのクレジット

于 2012-11-12T15:01:49.140 に答える
1

2番目の質問について

 B[ind(tid,1,Nel)]=j// j in most cases do no go all the way to the Nel reach

GPUで計算を実行する場合、同期の理由により、ジョブを終了したすべてのスレッドは、同じワークグループ内のすべてのスレッドが終了するまで計算を実行しません。

つまり、この計算を実行するために必要な時間は最悪の場合の時間になります。ほとんどのスレッドが完全にダウンしていなくても問題ありません。

最初の質問についてはよくわかりませんが、どのように時間を測定しますか?私はcudaにあまり精通していませんが、CPUからGPUにコピーする場合、実装はデータをバッファリングし、関連する有効時間を隠していると思います。

于 2012-11-12T09:52:53.603 に答える