【问题标题】:Problem measuring N times the execution time of a code block测量代码块执行时间 N 倍的问题
【发布时间】:2011-02-18 19:18:40
【问题描述】:

编辑:在写完这篇解释每一个小细节的长篇文章后,我才发现我的问题......如果有人能给我一个很好的答案,说明我做错了什么,我怎么能得到以秒为单位的执行时间(使用小数点后 5 位左右的浮点数),我将其标记为已接受。提示:问题在于我如何解释 clock_getttime() 手册页。

嗨,

假设我有一个名为myOperation 的函数,我需要测量它的执行时间。为了测量它,我使用了clock_gettime(),因为在其中一个 cmets 中推荐了here

我的老师建议我们测量它N 次,这样我们就可以获得最终报告的平均值、标准差和中值。他还建议我们执行myOperationM 次而不是一次。如果myOperation 是一个非常快的操作,那么测量它M 次可以让我们了解它所花费的“实时”;导致正在使用的时钟可能没有测量此类操作所需的精度。因此,执行myOperation 仅一次或M 次实际上取决于操作本身是否需要足够长的时间来满足我们使用的时钟精度。

我在处理 M 次执行时遇到了麻烦。增加M 会降低(很多)最终平均值。这对我来说没有意义。就是这样,从 A 点到 B 点平均需要 3 到 5 秒。但是从 A 点到 B 点再回到 A 点需要 5 次(这就是 10 次,因为 A 到 B 点与 B 点相同)到 A) 然后你测量它。除了除以 10,你得到的平均值应该与你从 A 点到 B 点的平均时间相同,也就是 3 到 5 秒。

这是我希望我的代码执行的操作,但它不起作用。如果我不断增加从 A 到 B 再返回 A 的次数,每次的平均值都会越来越低,这对我来说毫无意义。

足够的理论,这是我的代码:

#include <stdio.h>
#include <time.h>

#define MEASUREMENTS 1
#define OPERATIONS   1

typedef struct timespec TimeClock;

TimeClock diffTimeClock(TimeClock start, TimeClock end) {
    TimeClock aux;

    if((end.tv_nsec - start.tv_nsec) < 0) {
        aux.tv_sec = end.tv_sec - start.tv_sec - 1;
        aux.tv_nsec = 1E9 + end.tv_nsec - start.tv_nsec;
    } else {
        aux.tv_sec = end.tv_sec - start.tv_sec;
        aux.tv_nsec = end.tv_nsec - start.tv_nsec;
    }

    return aux;
}

int main(void) {
    TimeClock sTime, eTime, dTime;
    int i, j;

    for(i = 0; i < MEASUREMENTS; i++) {
        printf(" » MEASURE %02d\n", i+1);

        clock_gettime(CLOCK_REALTIME, &sTime);

        for(j = 0; j < OPERATIONS; j++) {
            myOperation();
        }

        clock_gettime(CLOCK_REALTIME, &eTime);

        dTime = diffTimeClock(sTime, eTime);

        printf("   - NSEC (TOTAL): %ld\n", dTime.tv_nsec);
        printf("   - NSEC (OP): %ld\n\n", dTime.tv_nsec / OPERATIONS);
    }

    return 0;
}

注意:上面的diffTimeClock函数来自这个blog post。我用myOperation() 替换了我的实际操作,因为发布我的实际功能没有任何意义,因为我必须发布长代码块,您可以轻松地编写myOperation() 任何您喜欢的代码,如果你愿意。

如您所见,OPERATIONS = 1 和结果是:

 » MEASURE 01
   - NSEC (TOTAL): 27456580
   - NSEC (OP): 27456580

对于OPERATIONS = 100,结果是:

 » MEASURE 01
   - NSEC (TOTAL): 218929736
   - NSEC (OP): 2189297

对于OPERATIONS = 1000,结果是:

 » MEASURE 01
   - NSEC (TOTAL): 862834890
   - NSEC (OP): 862834

对于OPERATIONS = 10000,结果为:

 » MEASURE 01
   - NSEC (TOTAL): 574133641
   - NSEC (OP): 57413

现在,我不是数学天才,实际上远非如此,但这对我来说没有任何意义。我已经和一个和我一起做这个项目的朋友讨论过这个问题,他也无法理解这些差异。我不明白为什么当我增加OPERATIONS 时价值越来越低。无论我执行多少次,操作本身都应该花费相同的时间(当然,平均而言,不是完全相同的时间)。

您可以告诉我,这实际上取决于操作本身、正在读取的数据以及某些数据可能已经在缓存中等等,但我认为这不是问题所在。在我的例子中,myOperation 正在从 CSV 文件中读取 5000 行文本,用; 分隔值并将这些值插入到数据结构中。对于每次迭代,我都会破坏数据结构并再次对其进行初始化。

现在我想起来了,我也认为clock_gettime() 测量时间有问题,也许我没有正确使用它。我的意思是,看看最后一个例子,OPERATIONS = 10000。总共花费了 574133641ns,大概是 0.5s;不可能,等了几分钟,我受不了看着屏幕等着去吃东西了。

【问题讨论】:

  • 由于您要测量两个事件之间的挂钟时间,您应该使用CLOCK_MONOTONIC 而不是CLOCK_REALTIME - 如果系统时间更改,后者可以跳转,前者不受影响。跨度>
  • 是的,但为什么不让我手动操作,系统时间会发生变化?
  • 因为像ntpd 这样的系统守护进程改变了它? (或者共享系统上的系统管理员?)
  • 好的,这在我的测试机器上不太可能发生。但你有你的意思。我只是使用 REALTIME 而不是 MONOTONIC,因为我读过一些机器(甚至一些 Linux 发行版)没有实现 MONOTONIC,我希望我的老师能够编译代码(以防他没有)。当然,如果是 Mac,clock_gettime() 甚至都不存在(反正不在我朋友的 Mac 上)。

标签: c time clock performance


【解决方案1】:

您只需更改您的diffTimeClock() 函数以返回秒数差,作为double

double diffTimeClock(TimeClock start, TimeClock end) {
    double diff;

    diff = (end.tv_nsec - start.tv_nsec) / 1E9;
    diff += (end.tv_sec - start.tv_sec);

    return diff;
}

并在主程序中将dTime 更改为double,以及适合的printfs:

printf("   - SEC (TOTAL): %f\n", dTime);
printf("   - SEC (OP): %f\n\n", dTime / OPERATIONS);

【讨论】:

    【解决方案2】:

    看起来 TimeClock 类型有两个字段,一个用于秒,一个用于纳秒。仅将 nanosec 字段除以操作数是没有意义的。您需要划分总时间。

    【讨论】:

      【解决方案3】:

      如果您使用的是具有 gettimeofday() 函数的 POSIX 系统,您可以使用类似这样的方法来获取当前时间(以微秒为单位):

      long long timeInMicroseconds(void) {
          struct timeval tv;
      
          gettimeofday(&tv,NULL);
          return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
      }
      

      这非常方便的原因是为了计算你的函数需要多少时间,你需要这样做:

      long long start = timeInMicroseconds();
      ... do your task N times ...
      printf("Total microseconds: %lld", timeInMicroseconds()-start);
      

      所以你不必处理两个整数,一个是秒,一个是微秒。加减时间会很明显。

      【讨论】:

      • 我不知道你是否意识到,但你做了你所说的我不必做的事情,“处理两个整数”。看看return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;。我做了类似的事情来解决我原来的问题,我的精度是纳米而不是微米。
      • 是的,我的意思是,当您需要将一个时间减去另一个时间时,不要明确处理两个整数,否则会更难检查纳秒的溢出(然后减去 1 秒和将 1000000 纳秒添加到原始结果)。相反,按照我建议的方式,您只在创建时间的函数中处理两个整数(以微秒为单位)。其余所有代码将只使用微不足道的单数。
      • 我已经在diffTimeClock() 这样做了,所以我不太明白你的帖子......
      【解决方案4】:

      我通常为此使用 time() 函数。它显示挂钟时间,但这才是我真正关心的最后。

      性能测试的一个问题是操作系统可能会缓存与文件系统相关的操作。所以第二次(及以后)运行可能比第一次运行快得多。您通常需要测试可能的操作并对结果进行平均,以更好地了解您所做的任何更改的结果。有很多变量可以帮助您过滤掉噪音。

      【讨论】:

        猜你喜欢
        • 2013-04-28
        • 1970-01-01
        • 2017-09-11
        • 2013-06-24
        • 1970-01-01
        • 2022-01-03
        • 1970-01-01
        • 2021-12-16
        相关资源
        最近更新 更多