使用cudaEvent定时****()VS clock_gettime()

时间:2015-08-20 13:12:04

标签: cuda timing

我正在尝试计算代码。我被告知可以应用cudaEvent ****()。与此同时,我的原始代码使用clock_gettime()来计时。我打印由cudaEvent ****()和clock_gettime()测量的结果,如下所示。这就是我真正感到困惑的事情。

由cudaEvent测量****()

  • init数据结构:1971.517578ms
  • 建立背景:0.007296ms
  • 重新排列数据:234.271423ms
  • 复制数据:53.402176ms
  • 时间步:17221.333984ms

由clock_gettime()

测量
  • init数据结构:1.802874s
  • 建立背景:20.541891s
  • 重新排列数据:0.235464s
  • 复制数据:0.051851s
  • 时间步:8.429955s

注意:

  • init数据结构:完全适用于CPU
  • 建立上下文:仅限一行:cudaFree((void *)0);
  • 重新排列数据:完全适用于CPU
  • 复制数据:将数据从主机传输到设备
  • 时间步进:涉及两个核心功能

Q1:由cudaEvent ****()(0.0072ms)测量的“建立上下文”花费的时间与clock_gettime()(~20.5s)测量的时间大不相同。实际上,这部分只有一行建立了一个上下文。 cudaFree(0) 这种巨大差异是如何发生的?

Q2:由cudaEvent ****()(~17.221s)测量的“时间步进”花费的时间是clock_gettime()(~8.43s)测得的时间的两倍。有人告诉我,异步可能是一个可能的原因,但我并没有真正得到它。任何人都可以帮助我度过难关吗?

问题3:花费的挂钟时间非常接近clock_gettime()测量的时间。但是,我被告知cudaEvent ****()在计算cuda代码时更可取。我不知道应该涂哪一个。

===============================更新=============== ==================== 以下是我的代码的一部分,其中定义了一些定时函数和宏。

#define TIMING 1
#if TIMING
double get_time()
{
    struct timespec time;
    clock_gettime(CLOCK_REALTIME, &time);
    return (double)time.tv_sec + (double)time.tv_nsec * 1.0e-9 ;
}
#endif
#define CUDATIMING 0
#if CUDATIMING
#define cuda_timing_init \
    cudaEvent_t startEvent, stopEvent;\
    float timeEvent;\
    cudaEventCreate(&startEvent);\
    cudaEventCreate(&stopEvent);
#define cuda_timing_begin \
    cudaEventRecord(startEvent, 0);
#define cuda_timing_stop(str) \
    cudaEventRecord(stopEvent, 0);\
    cudaEventSynchronize(stopEvent);\
    cudaEventElapsedTime(&timeEvent, startEvent, stopEvent);\
    printf("time spent of %s: %fms\n", str, timeEvent);
#define cuda_timing_destroy \
    cudaEventDestroy(startEvent);\
    cudaEventDestroy(stopEvent);
#endif

我使用这些函数和宏来计时。

===========================更新20150823 ================== =============

这是我的代码的基本结构,包括时间。我不确定它是否有助于解决我的时间问题。

void
copy_float_from_host_to_dev(float *h_p, float **d_pp, int size)
{
    if_error(cudaMalloc(d_pp, size));
    if_error(cudaMemcpy(*d_pp, h_p, size, cudaMemcpyHostToDevice));
}

void
copy_int_from_host_to_dev(int *h_p, int **d_pp, int size)
{
    if_error(cudaMalloc(d_pp, size));
    if_error(cudaMemcpy(*d_pp, h_p, size, cudaMemcpyHostToDevice));
}

int
main(int argc, char **argv)
{
    // init 
    // totally CPU codes        
    // ......
#if TIMING
    double t1, t2, t3, t4, t5, t6; 
    t1 = get_time();
#endif
#if CUDATIMING
    cuda_timing_init;
    cuda_timing_begin;
#endif
    // init data structure
    // totally CPU codes
    // ......
#if TIMING
    t2 = get_time();
#endif
#if CUDATIMING
    cuda_timing_stop("init data structure");
    cuda_timing_begin;
#endif
    // establish context
    cudaFree((void*)0);
#if TIMING
    t3 = get_time();
#endif
#if CUDATIMING
    cuda_timing_stop("establish context");
    cuda_timing_begin;
#endif
    // rearrange data
    // totally CPU codes
    // data on CPU side has different structure
    // compared to data on GPU side, so I need
    // to rearrange it.
    // ......
#if TIMING
    t4 = get_time();
#endif
#if CUDATIMING
    cuda_timing_stop("rearrange data");
    cuda_timing_begin;
#endif
    // copy data from host to device
    // about 10 copies. the following are 2 of them
       // all use copy_float/int_from_host_to_dev 
    // h_lap --> d_lap
    copy_float_from_host_to_dev(h_lap, &d_lap, lapsize); 
    // h_etol --> d_etol
    copy_int_from_host_to_dev(h_etol, &d_etol, etolsize); 
    // ......
#if TIMING
    t5 = get_time();
#endif
#if CUDATIMING
    cuda_timing_stop("copy data");
    cuda_timing_begin;
#endif
    // time stepping
    for(step = 1; step < para->nstep; step++)
    {
    /* kernel_1: matrix-vector multiplication.
     * The matrix is special, so multiplication 
     * can be very fast.  
     * atomic operations are involved
     * no data transfers between host and device */
    kernel_1<<<32768, 128>>>(......);
    /* kernel_2: vector operations.
     * Assuming that a,b,c,d are vectors,
     * what kernel_2 does is: a=2*a-b+c*d 
     * no data transfers between host and device */
    kernel_2<<<16384, 128>>>(......);
    }
#if TIMING
    t6 = get_time();
    printf("total time: %fs\n", t6-t1);
    printf("  init data structure: %fs\n", t2-t1);
    printf("  establish context: %fs\n", t3-t2);
    printf("  rearrange data: %fs\n", t4-t3);
    printf("  copy data: %fs\n", t5-t4);
    printf("  time stepping: %fs\n", t6-t5);
#endif
#if CUDATIMING
    cuda_timing_stop("time stepping");
    cuda_timing_destroy;
#endif

    // destroy data structure
    // totally CPU codes
    // ......

    return 0;
}

1 个答案:

答案 0 :(得分:1)

您只提供了一个代码示例,因此我只能提供一个答案:

  

由cudaEvent ****()(0.0072ms)测量的“建立上下文”花费的时间与clock_gettime()(~20.5s)测量的时间大不相同。实际上,这部分只有一行建立了一个上下文。 cudaFree(0)这种巨大的差异是如何发生的?

您假设cudaFree调用建立了CUDA上下文是不正确的。惰性上下文建立发生在需要直接与上下文交互的第一个调用。在这种情况下,您的事件计时代码正在建立上下文,因此cudaFree调用基本上是免费的。这就是为什么两种计时方法之间存在大的挂钟时间差异的原因。