如何在Linux下测量C程序的实际执行时间?

22

我知道这个问题可能已经被问过很多次,但是似乎大多数问题都涉及基于壁钟的代码流逝时间。代码块的流逝时间通常不等于实际执行时间,因为在感兴趣的代码流逝时间期间可能会执行其他进程。

我使用了getrusage()来获取进程的用户时间和系统时间,然后通过(用户时间+系统时间)计算出实际执行时间。我正在Ubuntu上运行我的程序。以下是我的问题:

  1. 我如何知道getrusage()的精度?
  2. 是否有其他方法可以比getrusage()提供更高的精度?

在正常的操作系统中,如果没有其他用户程序运行,实际执行时间和经过时间之间的差异就不重要了。通常情况下,实际执行时间与经过时间成正比。 - Andrei Bozantan
1
@Andrei:你假设程序不执行任何I/O操作(等待磁盘或网络),完全受CPU限制。这只适用于少数程序。 - Codo
1
定义“实际执行时间”。如果不是挂钟时间也不是用户+系统时间,那么它是什么?当你需要更高的精度时,你是指纳秒而不是微秒吗?请注意,纳秒级精度可能是虚构的,因为大多数Unix内核不提供这种东西 - tchrist
1
clock_gettime能否满足您的需求?它们为大多数要求提供了几个时钟。CLOCK_PROCESS_CPUTIME_ID可能是您需要的。 - user406009
2
在多CPU机器上的多线程程序中,进程的CPU时间可能比进程的经过时间高得多(高达N倍,其中N是独立处理器的数量)。 - Jonathan Leffler
3个回答

19

你可以通过利用内核的CPU时间功能,在Linux上检查一个进程的实际CPU时间:

 #include <time.h>

 clock_t start, end;
 double cpu_time_used;

 start = clock();
 ... /* Do the work. */
 end = clock();
 cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;

来源: http://www.gnu.org/s/hello/manual/libc/CPU-Time.html#CPU-Time

这样,您可以计算进程所使用的 CPU 时钟周期或实际被 CPU 处理的指令数量,从而获得实际工作时间。


这个方法似乎是测量两次调用clock()之间的经过时间,也就是说,测量的时间可能包括花费在其他进程上的时间,我理解的对吗? - Dillon Geo
3
@Dillon Geo: 不是的。这是在两次调用之间由调用进程消耗的CPU时间。 - caf
1
@caf 是正确的 - 引用主CPU时间页面:CPU时间类似于日历时间,但它基于时间连续体的子集,当特定进程正在积极使用CPU时。因此,CPU时间是相对于进程的。 如果我的答案有帮助并且是正确的,请您点赞/接受为解决方案吗?谢谢! :) - Lars
在64位机器上,clock()可能是最好的解决方案,因为它可以基于最佳系统调用进行实现,但这取决于具体实现。根据Linux clock(3)手册:“在glibc 2.17及更早版本中,clock()是基于times(2)实现的。为了提高精度,自glibc 2.18以来,它是基于clock_gettime(2)实现的(使用CLOCK_PROCESS_CPUTIME_ID时钟)。” - vinc17

5

getrusage()函数是我所知道的获取“已消耗CPU时间”的唯一标准/可移植方式。

目前没有简单的方法来确定返回值的精度。我建议先调用getrusage()获取初始值,然后重复调用它,直到返回的值与初始值不同为止,然后假设有效精度是初始值和最终值之间的差异。这虽然有些取巧(精度可能高于此方法确定的值,并且结果可能被视为最坏情况下的估计),但总比没有好。

我还担心返回值的准确性。在某些内核下,当定时器IRQ发生时,计数器会增加任意正在运行的代码;因此,一个进程可能非常幸运(并连续阻塞在定时器IRQ发生之前),或非常不幸(并在定时器IRQ发生之前解除阻止)。在这种情况下,“幸运”可能意味着CPU占用高的看起来像没有使用CPU时间,“不幸”可能意味着使用非常少的CPU时间的进程看起来像CPU占用很高。

针对特定体系结构/版本的特定内核(取决于内核在某些情况下是否使用特定配置选项进行编译),可能存在更高精度的非标准可移植替代品...


0
你可以使用这段代码:
#include <sys/time.h>
struct timeval start, end;
gettimeofday(&start, NULL);
.
.
.
gettimeofday(&end, NULL);
delta = ((end.tv_sec  - start.tv_sec) * 1000000u +
         end.tv_usec - start.tv_usec) / 1.e6;
printf("Time is : %f\n",delta);

它将显示您的代码片段的执行时间


1
因为这里使用了gettimeofday()来测量墙上时钟时间,所以被踩了。OP要求CPU时间(即当进程未执行时,经过的时间计数器应停止)。 - Matthew Cole

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接