CUDA程序不测量执行时间:cudaEventRecord

时间:2017-12-17 16:25:09

标签: c++ cuda

我需要真正理解CUDA如何测量时间执行。

让我们关注这一部分(如果你想测试它,整个代码就在消息的末尾)。

// Launching Kernel and measuring its time
    cudaEventRecord(startComputation);
    MatProd << <BlockPerGrid, ThreadPerBlock >> >(C, A, B, dimAx, dimBx, dimCx, dimCy);
    cudaEventRecord(stopComputation);

    //cudaEventSynchronize(stopComputation); // this line must be HERE and it returns me a good computation time.

    cudaEventElapsedTime(&millisecondsPureComputation, startComputation, stopComputation);

    cudaDeviceSynchronize(); // putting this doesn't do the job
    //cudaEventSynchronize(stopComputation); // if I put it here instead it doesn't work.

    std::cout << "Computation time : " << millisecondsPureComputation << "ms" << std::endl;

我如何理解这些事情。程序运行时,CPU多次调用内核。 cudaEventRecord,MatProd和cudaEventElapsedTime 都在GPU上执行

我的两个cudaEventRecord之间的时间是在我的cudaEventElapsedTime中计算的。

问题是:如果我的CPU在GPU计算方面太快,变量millisecondsPureComputaion将保持它的初始值:0。

因此我必须在显示计算之前向CPU说“等待GPU完成了cudaEventElapsedTime”。通过这种方式,变量millisecondsPureComputation将具有我们想要的值。

因此,放一个cudaDeviceSynchronise();就在cudaEventElapsedTime之后就足够了。

但实际上它在我这样做时不起作用,变量仍为0.唯一的方法是将非零数字放在cudaEvntSynchronize(stopComputation)之前 cudaEventElapsedTime和I不明白为什么。

我的问题:

为什么我的方法放一个cudaDeviceSynchronise();不起作用? 你能解释一下为什么要放一个cudaEventSynchronize(stopComputation);在cudaEventElapsedTime工作之前?它有什么特别之处呢?

#include <iostream>
#include <math.h>
#include <chrono>

__global__  void MatProd(float* C, float* A, float*B, int dimAx, int dimBx, int dimCx, int dimCy)
{
    int row = blockDim.y*blockIdx.y + threadIdx.y;
    int col = blockDim.x*blockIdx.x + threadIdx.x;

    double Result = 0;

    if (row <= dimCy - 1 && col <= dimCx - 1)
    {
        for (int k = 0; k < dimAx; k++)
        {
            Result += A[k + dimAx*row] * B[col + dimBx*k];
        }

        C[col + row*dimCx] = Result;
    }
}

int main(void)
{
    /* Initializing the inputs */
    // Matrix sizes
    int dimAx = 100;
    int dimAy = 100;
    int dimBx = 2;

    int dimBy = dimAx;
    int dimCx = dimBx;
    int dimCy = dimAy;

    // Matrix pointers
    float *A, *B, *C;

    // Variable to measure CUDA time execution.
    float millisecondsPureComputation = 0;
    cudaEvent_t startComputation, stopComputation;
    cudaEventCreate(&startComputation);
    cudaEventCreate(&stopComputation);

    // Memory allocation
    cudaMallocManaged(&A, dimAx*dimAy*sizeof(float));
    cudaMallocManaged(&B, dimBx*dimBy*sizeof(float));
    cudaMallocManaged(&C, dimCx*dimCy*sizeof(float));

    // Initializing matrices
    for (int i = 0; i < dimAy; i++)
    {
        for (int j = 0; j < dimAx; j++) 
        {
            A[j + dimAx*i] = j + 10 * i;
        }
    }
    for (int i = 0; i < dimBy; i++)
    {
        for (int j = 0; j < dimBx; j++)
        {
            B[j + dimBx*i] = (j + 1)*pow(i, 2);
        }
    }

    // Kernel properties

    int threadPerBlockx = 32;
    int threadPerBlocky = 32;
    int BlockPerGridx = 1 + (dimCx - 1) / threadPerBlockx;
    int BlockPerGridy = 1 + (dimCy - 1) / threadPerBlockx;


    dim3 BlockPerGrid(BlockPerGridx, BlockPerGridy, 1);
    dim3 ThreadPerBlock(threadPerBlockx, threadPerBlocky, 1);

    // Launching Kernel and measuring its time
    cudaEventRecord(startComputation);
    MatProd << <BlockPerGrid, ThreadPerBlock >> >(C, A, B, dimAx, dimBx, dimCx, dimCy);
    cudaEventRecord(stopComputation);

    //cudaEventSynchronize(stopComputation); // this line must be HERE and it returns me a good computation time.

    cudaEventElapsedTime(&millisecondsPureComputation, startComputation, stopComputation);

    cudaDeviceSynchronize(); // putting this doesn't do the job
    //cudaEventSynchronize(stopComputation); // if I put it here instead it doesn't work.

    std::cout << "Computation time : " << millisecondsPureComputation << "ms" << std::endl;

    cudaFree(A);
    cudaFree(B);
    cudaFree(C);

    return 0;
}

[edit]我把代码更改为它,现在它可以正常工作,但我仍然不明白发生了什么......

cudaEventRecord(startComputation);
        MatProd << <BlockPerGrid, ThreadPerBlock >> >(C, A, B, dimAx, dimBx, dimCx, dimCy);

        //cudaDeviceSynchronize();
        cudaEventRecord(stopComputation);
        cudaDeviceSynchronize();
        cudaEventElapsedTime(&millisecondsPureComputation, startComputation, stopComputation);

我的问题在这里:

  • 所以,cudaEventRecord(),cudaEventElapsedTime()在我的情况下在主机上执行,如果我理解的那样(文档中__host__的bc)。

在doc上他们说cudaEventRecord在事件中捕获了蒸汽的内容。我们称之为流的“内容”并不完全清楚。

但是我不知道它是如何工作的。实际上,如果MatProd需要很长时间,CPU将在GPU完成其工作之前到达第二个cudaEventRecord。所以我应该得到一个错误的结果......?

我这样说是因为你解释我这些API函数是在主机上执行的。所以根据我的理解,它们将与内核并行启动。当我们在两个cudaEventRecord()之后同步 之后,我应该得到一个错误的结果......?

也许是因为我并没有真正明白你在主机上执行的意思,但我把它理解为在CPU上启动的功能(因此,它不需要等待内核完成)。

1 个答案:

答案 0 :(得分:1)

在设备上执行的唯一事情是代码前面有__global____device__。其他所有内容,包括CUDA运行时API调用,以及实际的内核启动本身,都是主机代码。

由于尚未发生(第二个)事件,因此您将获得零。

请阅读cudaEventElapsedTime的{​​{3}}:

  

如果两个事件都调用了cudaEventRecord(),但其中一个或两个尚未完成(即cudaEventQuery()将至少在其中一个事件上返回cudaErrorNotReady) ,cudaErrorNotReady被退回。

这就是你的情况所发生的事情,既然你没有the documentation,你就会对此视而不见。当两个事件都没有完成时(这意味着CUDA执行流没有到达两个事件),cudaEventElapsedTime()调用除了返回CUDA错误外不执行任何操作。

如果在cudaDeviceSynchronize()调用之前发出cudaEventSynchronize()调用或相应的cudaEventElapsedTime()调用,则会强制CPU线程在此时等待,直到事件完成。这将满足cudaEventElapsedTime()通话的必要条件,您将获得合理的经过时间值。

添加进一步说明。我们一步一步地考虑这个问题。

  1. 在时间段1中,CPU代码&#34;记录&#34;由于此调用,startComputation事件进入CUDA执行流:cudaEventRecord(startComputation); CUDA处理器(GPU)处于空闲状态。因此,此时,特定的CUDA事件startComputation被视为&#34;记录&#34;但不是&#34;已完成&#34;
  2. 在时间段2中,CPU线程向前移动到上一次cudaEventRecord调用之后的下一个项目,即内核启动:MatProd << <BlockPerGrid, ThreadPerBlock >> >(...)。在此期间,CPU将内核启动作为要在CUDA执行流中处理的下一个项目。由于上面时间段1中的活动,CUDA处理器(GPU)有工作要做,所以它开始处理事件。对事件的处理将事件从&#34; RECORDED&#34;声明&#34;已完成&#34;州。
  3. 在时间段3中,CPU线程向前移动到上一次内核启动后的下一个项目,这是另一个事件记录调用:cudaEventRecord(stopComputation);就像在时间段1中一样,这会将事件放入CUDA流中执行,在内核执行完成后进行处理。因此,这个新活动在&#34; RECORDED&#34;国家但不是&#34;已完成&#34;州。在此时间段3期间,GPU开始执行内核并正忙于执行内核。
  4. 在时间段4中,CPU线程前进到上一个事件记录调用之后的下一个项目,这是对运行时API的请求,以在两个事件(cudaEventElapsedTime)之间进行测量。为了进行这种测量,两个事件都必须在&#34;完成&#34;州。在此时间段4期间,GPU仍然忙于处理内核,因此它没有前进来处理{&#34;记录&#34}的stopComputation事件。但不是&#34;已完成&#34;在时间段3中。因此,两个事件中的第一个(startComputation)位于&#34;已完成&#34;状态,但两个事件中的第二个(stopComputation)仍然在&#34;记录&#34;州。因此,cuda运行时API调用(如已指示)将返回错误,并且不会给出合理的度量。 要求两个事件都在&#34;已完成&#34;在它返回所要求的测量值之前的状态。
  5. 那么在您修改后的代码中有什么不同,并且在经过时间请求之前包含同步函数?让我们在上面的时间段3结束后接收我们的时间线重播,因为到目前为止的一切都没有改变。但现在时间段4不同了:

    1. 在时间段4中,CPU线程向前移动以处理CUDA事件记录调用之后的下一个项目,但该指令是同步指令(cudaDeviceSynchronize())。在此时间段4期间,GPU仍在忙于处理内核。由于CUDA时间轴/流仍有工作要做,因此CPU线程在同步步骤中停止。它坐在那里等待。

    2. 在时间段5中,GPU仍在忙于处理内核。 CPU线程在cudaDeviceSynchronize()调用时等待。

    3. 在时间段6中,GPU仍在忙着处理内核。 CPU线程在cudaDeviceSynchronize()调用时等待。

    4. 在时间段7中,GPU完成对内核的处理,并继续前进到CUDA流中记录的下一个工作,即cuda事件stopComputation。此事件的处理将stopComputation的状态转换为&#34; RECORDED&#34;到&#34;已完成&#34;。由于GPU在时间段7期间仍在执行某些操作,因此CPU线程在cudaDeviceSynchronize()调用时仍处于等待状态。

    5. 在时间段8中,GPU已经完成了对其发出的所有工作的处理并返回到空闲状态。因此,CPU不再需要在cudaDeviceSynchronize()调用时等待,因此它将继续执行CPU线程中的下一个项目,即经过时间测量的请求。作为先前活动的结果,两个事件(startComputationstopComputation)都在&#34;完成&#34;状态,因此事件经过时间测量请求是合法的,并且呼叫将返回合理的测量(并且没有错误)。