【问题标题】:clock_gettime() Vs. gettimeofday() for measuring OpenMP execution timeclock_gettime() 对比gettimeofday() 用于测量 OpenMP 执行时间
【发布时间】:2017-02-04 05:54:22
【问题描述】:

我正在编写一些 C 代码,该代码实现了三重嵌套 for 循环,以计算矩阵-矩阵乘法,同时使用 OpenMP 对其进行并行化。我试图准确测量从 for 循环开始到结束所花费的时间。到目前为止,我一直在使用 gettimeofday(),但我注意到有时感觉它并没有准确记录 for 循环执行所花费的时间。好像是在说比实际花费的时间更长。

这是原始代码:

struct timeval start end;
double elapsed;

gettimeofday(&start, NULL);
#pragma omp parallel for num_threads(threads) private(i, j, k)
for(...)
{
 ...
 for(...)
 {
  ...
  for(...)
  {
   ...
  }
 }
}

gettimeofday(&end, NULL);
elapsed = (end.tv_sec+1E-6*end.tv_usec) - (start.tv_sec+1E-6*start.tv_usec)

下面是使用clock_gettime()的相同代码:

 struct timespec start1, finish1;
 double elapsed1;

clock_gettime(CLOCK_MONOTONIC, &start1);

  #pragma omp parallel for num_threads(threads) private(i, j, k)
    for(...)
    {
     ...
     for(...)
     {
      ...
      for(...)
      {
       ...
      }
     }
    }

clock_gettime(CLOCK_MONOTONIC, &finish1);
elapsed1 = (finish1.tv_sec - start1.tv_sec);
elapsed1 += (finish1.tv_nsec - start1.tv_nsec)/1000000000.0;

循环需要 3-4 秒才能完成,我尝试同时使用两个时间测量值,使用 gettimeofday() 的结果几乎总是比 clock_gettime() 的结果长,有时比我使用clock_gettime()得到的结果:

struct timespec start1, finish1;
double elapsed1;

struct timeval start end;
double elapsed;

clock_gettime(CLOCK_MONOTONIC, &start1);
gettimeofday(&start, NULL);

  #pragma omp parallel for num_threads(threads) private(i, j, k)
    for(...)
    {
     ...
     for(...)
     {
      ...
      for(...)
      {
       ...
      }
     }
    }

gettimeofday(&end, NULL);
clock_gettime(CLOCK_MONOTONIC, &finish1);

elapsed = (end.tv_sec+1E-6*end.tv_usec) - (start.tv_sec+1E-6*start.tv_usec)

elapsed1 = (finish1.tv_sec - start1.tv_sec);
elapsed1 += (finish1.tv_nsec - start1.tv_nsec)/1000000000.0;

这是有原因的吗?使用这两个功能时可能会导致什么不同?我试图更好地理解这两个函数的性质。

【问题讨论】:

  • 我还分别使用两个时间函数运行了代码,然后也注意到了时间差。
  • 第1步:测试后,打印读取的值。
  • 只要使用omp_get_wtime()!
  • 发布代码报告差异的方式会有所帮助。尤其是在多长时间内。
  • 请发布minimal reproducible example 以及该代码的具体结果,而不仅仅是您的摘要。

标签: c time parallel-processing openmp gettimeofday


【解决方案1】:

elapsed = (end.tv_sec+1E-6*end.tv_usec) - (start.tv_sec+1E-6*start.tv_usec) 在减去都很大的相似值时容易丢失精度。

  1. 使用elapsed = (end.tv_sec - start.tv_sec) - (start.tv_usec- end.tv_usec)/1E6。这就像 OP 的第 2 和第 3 代码,但不是第一个。

  2. 保持公平,以一致的顺序安排时间以消除偏见。

    clock_gettime(CLOCK_MONOTONIC, &start1);
    gettimeofday(&start, NULL);
    
    ...
    
    // gettimeofday(&end, NULL);
    // clock_gettime(CLOCK_MONOTONIC, &finish1);
    clock_gettime(CLOCK_MONOTONIC, &finish1);
    gettimeofday(&end, NULL);
    
  3. 次要:第三个非常细微的改进有助于减少一点位(0.5 微秒)的不一致性,即在刻度变化时开始测试。但请注意 @Dietrich Epp comment 以获得替代改进。

    gettimeofday(&t, NULL);
    do { 
      gettimeofday(&start, NULL);
    } while (start == t);
    

或者,使用宽整数数学来避免精度问题

long long elapsed_ns = (1LL*finish1.tv_sec - start1.tv_sec)*1000000000LL + 
    finish1.tv_nsec - start1.tv_nsec;

【讨论】:

  • 似乎带有刻度变化的技巧将采用统一的±0.5μsec误差,可以通过多次运行来减少,并用量化误差代替,不能通过多次减少运行。
  • 另一方面,0.5 µsec 肯定远低于此测量的噪声阈值。
  • @Dietrich Epp 好点。因此,对于少数运行,它保留了一些价值,但正如您所指出的,其他有价值的技术可以用于重复运行。
  • 这如何解决 >1 s 差异的问题?
  • @Zulan 如果 OP 使用的 double 的精度低于许多平台通常使用的精度,则 #1 中提到的计算顺序很重要。 IMO,该计算有问题,值得审查。
【解决方案2】:

感谢大家的帮助。原来问题与时间函数无关,而是由于在计算结束时间和开始时间之间的差异时没有正确放置括号。我知道,这是一种虎头蛇尾和愚蠢的解决方案,但它解决了我的问题。当我使用 gettimeofday() 时,我这样做是为了计算结果:

经过 = end.tv_sec+1E-6*end.tv_usec - start.tv_sec+1E-6*start.tv_usec

什么时候我应该这样做:

经过 = (end.tv_sec+1E-6*end.tv_usec) - (start.tv_sec+1E-6*start.tv_usec)

我使用的代码是由其他人编写的,用于使用 gettimeofday() 函数,他们有这些#define:

#define TIME_GET(time) (time).tv_sec+1E-6*(time).tv_usec
#define TIME_GET_RESULT(start,end) TIME_GET(end)-TIME_GET(start)

通过添加括号更改第一个#define 解决了我的问题:

#define TIME_GET(time) ((time).tv_sec+1E-6*(time).tv_usec)

当我开始使用 clock_gettime() 时,我正确地输入了括号,只是没有注意到代码作者为 gettimeofday() 设置的#define 不是。

【讨论】:

猜你喜欢
  • 2012-09-05
  • 1970-01-01
  • 2022-01-03
  • 1970-01-01
  • 2012-07-29
  • 2021-03-01
  • 2017-05-31
  • 1970-01-01
相关资源
最近更新 更多