使用事件计时CUDA应用程序

时间:2011-08-05 16:01:53

标签: timer cuda

我使用以下两个函数来计算我的代码的不同部分(cudaMemcpyHtoD,内核执行,cudaMemcpyDtoH)(包括同一GPU上的multi-gpus,并发内核,内核的顺序执行等)。据我所知,这些函数会记录事件之间经过的时间,但我想在代码的生命周期中插入事件可能会导致开销和不准确。我想听听批评意见,改善这些功能的一般建议以及对他们的警告。

//Create event and start recording
cudaEvent_t *start_event(int device, cudaEvent_t *events, cudaStream_t streamid=0)
{
        cutilSafeCall( cudaSetDevice(device) );
        cutilSafeCall( cudaEventCreate(&events[0]) );
        cutilSafeCall( cudaEventCreate(&events[1]) );
        cudaEventRecord(events[0], streamid);

    return events;
 }

 //Return elapsed time and destroy events
 float end_event(int device, cudaEvent_t *events, cudaStream_t streamid=0)
 {

        float elapsed = 0.0;
        cutilSafeCall( cudaSetDevice(device) );
        cutilSafeCall( cudaEventRecord(events[1], streamid) );
        cutilSafeCall( cudaEventSynchronize(events[1]) );
        cutilSafeCall( cudaEventElapsedTime(&elapsed, events[0], events[1]) );

        cutilSafeCall( cudaEventDestroy( events[0] ) );
        cutilSafeCall( cudaEventDestroy( events[1] ) );

        return elapsed;
 }

用法:

cudaEvent_t *events;
cudaEvent_t event[2]; //0 for start and 1 for end
...
events = start_event( cuda_device, event, 0 );
<Code to time>
printf("Time taken for the above code... - %f secs\n\n", (end_event(cuda_device, events, 0) / 1000) );

1 个答案:

答案 0 :(得分:9)

首先,如果这是用于生产代码,您可能希望能够在第二个cudaEventRecord和cudaEventSynchronize()之间执行某些操作。否则,这可能会降低您的应用与GPU和CPU工作重叠的能力。

接下来,我会将事件创建和销毁与事件记录分开。我不确定费用,但一般来说你可能不想经常调用cudaEventCreate和cudaEventDestroy。

我要做的是创建一个像这样的类

class EventTimer {
public:
  EventTimer() : mStarted(false), mStopped(false) {
    cudaEventCreate(&mStart);
    cudaEventCreate(&mStop);
  }
  ~EventTimer() {
    cudaEventDestroy(mStart);
    cudaEventDestroy(mStop);
  }
  void start(cudaStream_t s = 0) { cudaEventRecord(mStart, s); 
                                   mStarted = true; mStopped = false; }
  void stop(cudaStream_t s = 0)  { assert(mStarted);
                                   cudaEventRecord(mStop, s); 
                                   mStarted = false; mStopped = true; }
  float elapsed() {
    assert(mStopped);
    if (!mStopped) return 0; 
    cudaEventSynchronize(mStop);
    float elapsed = 0;
    cudaEventElapsedTime(&elapsed, mStart, mStop);
    return elapsed;
  }

private:
  bool mStarted, mStopped;
  cudaEvent_t mStart, mStop;
};

注意我没有包含cudaSetDevice() - 在我看来应该留给使用这个类的代码,以使其更灵活。用户必须确保在调用启动和停止时同一设备处于活动状态。

PS:NVIDIA并不打算将CUTIL作为生产代码的依赖 - 它仅仅是为了方便我们的示例而使用,并没有像CUDA库和编译器本身那样经过严格测试或优化。我建议你把像cutilSafeCall()这样的东西提取到你自己的库和标题中。