C程序的执行时间


209

我有一个旨在在多个处理器上并行运行的C程序。我需要能够记录执行时间(可能在1秒到几分钟之间的任何时间)。我已经搜索了答案,但是它们似乎都建议使用该clock()函数,然后使用该函数来计算程序所花费的时钟数除以该Clocks_per_second值。

我不确定如何Clocks_per_second计算值?

在Java中,我只是以毫秒为单位来计算执行前后的当前时间。

C中有类似的东西吗?我看了一下,但是似乎找不到一种比第二种分辨率更好的方法。

我也知道可以使用探查器,但是我希望自己实现一个计时器。

谢谢


3
您正在使用/可用哪些OS / API框架?只是普通的C?
typo.pl 2011年

4
这是一个相当小的程序,只是普通的C语言
Roger

我已经写在有关实现这个答案的便携式解决方案的详细信息:stackoverflow.com/questions/361363/...
亚历山大Saprykin

执行一个完整功能所需的时间stackoverflow.com/a/40380118/6180077
Abdullah Farweez '17

Answers:


344

CLOCKS_PER_SEC是在中声明的常量<time.h>。要获取C应用程序中任务使用的CPU时间,请使用:

clock_t begin = clock();

/* here, do your time-consuming job */

clock_t end = clock();
double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;

请注意,这会将时间作为浮点类型返回。这可能比一秒更精确(例如,您测量4.52秒)。精度取决于架构;在现代系统上,您很容易获得10ms或更短的时间,但是在较旧的Windows计算机上(从Win98时代开始)则接近60ms。

clock()是标准C;它可以在任何地方工作。有特定getrusage()于系统的功能,例如在类似Unix的系统上。

Java System.currentTimeMillis()不能衡量同一件事。这是一个“挂钟”:它可以帮助您测量程序执行所花费的时间,但是它不会告诉您使用了多少CPU时间。在多任务系统(即所有系统)上,它们可以有很大不同。


1
它给了我非常随机的结果-我在同一段代码中混合了大/小/负数。GCC 4.7 Linux 3.2 AMD64

3
是:clock()以某种内部标度返回一个称为“时钟”的时间,它CLOCKS_PER_SEC是每秒的时钟数,因此,除以CLOCKS_PER_SEC得出的时间以秒为单位。在上面的代码中,该值为a,double因此您可以随意缩放它。
Thomas Pornin 2015年

18
重要警告:clock()返回OS运行进程所花费的时间,而不是实际经过的时间。但是,这对于计时代码块是很好的,但不能测量现实世界中的时间流逝。

2
他说他想测量一个多线程程序。我不确定clock()是否适合此操作,因为它会汇总所有线程的运行时间,因此结果看起来像是代码按顺序运行。对于此类事情,我使用omp_get_wtime(),但我当然需要确保系统不忙于其他进程。
Youda008

1
我应该提到一些事情,即使该线程在一年前更相关:CLOCKS_PER_SEC是一个long int带有值的1000000,未划分时以毫秒为单位;不是CPU时钟周期。因此,它不需要考虑动态频率,因为这里的时钟以微秒为单位(也许是1 MHz CPU的时钟周期?)我做了一个简短的C程序打印了该值,而在我的i7-2640M笔记本电脑上它是1000000,动态频率允许800 MHz至2.8 GHz,即使使用Turbo Boost时也可以高达3.5 GHz。
DDPWNAGE '17

111

如果使用Unix Shell运行,则可以使用time命令。

在做

$ time ./a.out

假设a.out作为可执行文件将给您运行该代码所需的时间


3
@acgtyrant但仅限于简单的程序,因为它会需要整个节目时间,包括输入,输出等
phuclv

1
如果您使用的是Linux,并且已将(微)基准降低为启动开销可以忽略的程序,例如,一个静态可执行文件运行了几秒钟的热循环,则可以perf stat ./a.out用来获取高速缓存未命中的硬件性能计数器和分支机构的错误预测,以及IPC。
彼得·科德斯

61

在普通香草C中:

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

int main()
{
    clock_t tic = clock();

    my_expensive_function_which_can_spawn_threads();

    clock_t toc = clock();

    printf("Elapsed: %f seconds\n", (double)(toc - tic) / CLOCKS_PER_SEC);

    return 0;
}

6
我已经见过的最好的变量名了。tic =“ time in clock”,toc =“超时时钟”。而且也是tic-toc =“ tick-tock”。这就是我从现在开始标注时间的方法。
Logan Schelly

60

您从功能上希望这样:

#include <sys/time.h>

struct timeval  tv1, tv2;
gettimeofday(&tv1, NULL);
/* stuff to do! */
gettimeofday(&tv2, NULL);

printf ("Total time = %f seconds\n",
         (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 +
         (double) (tv2.tv_sec - tv1.tv_sec));

请注意,这以微秒为单位,而不仅仅是秒。


2
MinGW编译器基于GCC。这样就可以了。但是,如果您使用可视C编译器,则会出现错误。
user2550754

11
是的,它将在带有支持gettimeofday调用的ac库的Windows上运行。实际上,编译器是什么都没有关系,您只需要将其链接到适当的libc库即可。对于mingw,这不是默认的Windows 1。
Wes Hardaker

1
在Windows XP和cygwin gcc和Linux Ubuntu上,这对我有效。这就是我想要的。
爱与和平-Joe Codeswell,2015年

gettimeofday已过时,不建议用于新代码。它的POSIX手册页建议使用clock_gettime,它可以让您要求CLOCK_MONOTONIC不受系统时钟更改的影响,因此最好使用间隔时间。(请参阅JohnSll的答案)。例如,在现代Linux系统上,gettimeofday基本上是clock_gettime的包装器,该包装器将纳秒转换为微秒。
彼得·科德斯

12

大多数简单程序的计算时间都以毫秒为单位。所以,我想,您会发现这很有用。

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

int main(){
    clock_t start = clock();
    // Execuatable code
    clock_t stop = clock();
    double elapsed = (double)(stop - start) * 1000.0 / CLOCKS_PER_SEC;
    printf("Time elapsed in ms: %f", elapsed);
}

如果您要计算整个程序的运行时,并且您使用的是Unix系统,请使用以下time命令运行程序time ./a.out


在Windows中,至少该因子至少为100,但不是1000,并且它不是精确的
boctulus

6
这个答案不添加任何东西,这不是在亚历山大ç答案从两个年度的同期。
乔纳森·莱夫勒

3
@boctulus:在Windows上,1s 始终为 1000ms。
ALK

9

很多答案已经暗示clock(),然后CLOCKS_PER_SECtime.h。这可能是一个坏主意,因为这是我的/bits/time.h文件所说的:

/* ISO/IEC 9899:1990 7.12.1: <time.h>
The macro `CLOCKS_PER_SEC' is the number per second of the value
returned by the `clock' function. */
/* CAE XSH, Issue 4, Version 2: <time.h>
The value of CLOCKS_PER_SEC is required to be 1 million on all
XSI-conformant systems. */
#  define CLOCKS_PER_SEC  1000000l

#  if !defined __STRICT_ANSI__ && !defined __USE_XOPEN2K
/* Even though CLOCKS_PER_SEC has such a strange value CLK_TCK
presents the real value for clock ticks per second for the system.  */
#   include <bits/types.h>
extern long int __sysconf (int);
#   define CLK_TCK ((__clock_t) __sysconf (2))  /* 2 is _SC_CLK_TCK */
#  endif

因此CLOCKS_PER_SEC可能会定义为1000000,具体取决于您用于编译的选项,因此,这似乎不是一个好的解决方案。


1
感谢您提供的信息,但是还有更好的选择吗?
ozanmuyes 2014年

4
这不是一个普遍的问题:是的,Posix系统始终具有CLOCK_PER_SEC==1000000,但是同时,它们的Clock()实现都使用1-µs精度;顺便说一句,它具有减少共享问题的良好属性。如果要测量可能非常快的事件,例如1 ms以下,那么您首先应该担心clock()函数的精度(或分辨率),该精度必须比Posix中的1µs粗,但通常也要粗得多;通常的解决方案是多次运行测试;但是,所询问的问题似乎并不需要。
AntoineL 2015年

为什么这不是一个好的解决方案?如果从中clock()除以该值,则可以从中获得一些值,CLOCK_PER_SEC可以确保获得以秒为单位的CPU时间。测量实际时钟速度的责任是clock()功能的责任,而不是您的责任。
扎菲

9

Thomas Pornin作为宏的答案:

#define TICK(X) clock_t X = clock()
#define TOCK(X) printf("time %s: %g sec.\n", (#X), (double)(clock() - (X)) / CLOCKS_PER_SEC)

像这样使用它:

TICK(TIME_A);
functionA();
TOCK(TIME_A);

TICK(TIME_B);
functionB();
TOCK(TIME_B);

输出:

time TIME_A: 0.001652 sec.
time TIME_B: 0.004028 sec.

4

您必须考虑到,测量程序执行所花费的时间在很大程度上取决于机器在特定时刻的负载。

知道,以C方式获取当前时间的方法可以通过多种方式实现,一种更简单的方法是:

#include <time.h>

#define CPU_TIME (getrusage(RUSAGE_SELF,&ruse), ruse.ru_utime.tv_sec + \
  ruse.ru_stime.tv_sec + 1e-6 * \
  (ruse.ru_utime.tv_usec + ruse.ru_stime.tv_usec))

int main(void) {
    time_t start, end;
    double first, second;

    // Save user and CPU start time
    time(&start);
    first = CPU_TIME;

    // Perform operations
    ...

    // Save end time
    time(&end);
    second = CPU_TIME;

    printf("cpu  : %.2f secs\n", second - first); 
    printf("user : %d secs\n", (int)(end - start));
}

希望能帮助到你。

问候!


4

(如果您的系统管理员更改了系统时间,或者时区的冬季时间和夏季时间不同,则这里缺少所有答案。因此...)

在Linux上使用:clock_gettime(CLOCK_MONOTONIC_RAW, &time_variable); 如果系统管理员更改时间,或者您所在的国家/地区的冬季时间不同于夏季时间,则不会受到影响。

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

#include <unistd.h> /* for sleep() */

int main() {
    struct timespec begin, end;
    clock_gettime(CLOCK_MONOTONIC_RAW, &begin);

    sleep(1);      // waste some time

    clock_gettime(CLOCK_MONOTONIC_RAW, &end);

    printf ("Total time = %f seconds\n",
            (end.tv_nsec - begin.tv_nsec) / 1000000000.0 +
            (end.tv_sec  - begin.tv_sec));

}

man clock_gettime 状态:

CLOCK_MONOTONIC
              Clock  that  cannot  be set and represents monotonic time since some unspecified starting point.  This clock is not affected by discontinuous jumps in the system time
              (e.g., if the system administrator manually changes the clock), but is affected by the incremental adjustments performed by adjtime(3) and NTP.

您能解释一下用来获取秒数的计算吗?目前尚不清楚发生了什么。
科林·基南

1
这是不是(end.tv_nsec - begin.tv_nsec) / 1000000000.0导致0始终?
ALK

@alk:没有由分割double字面触发器int或longdouble转换之前的划分。当然,您可以只保留整数并打印该tv_sec部分,然后打印具有零的小数部分,例如%ld.%09ld,但是转换为double十分容易,并且53位精度对于基准时间而言通常就足够了。
彼得·科德斯

1
(糟糕,纳秒部分的减法可能需要加到秒部分,因此使用double并将其设为负数可以避免该问题。要使用纯整数格式的字符串,您需要timespec_subtracttimeval_subtractglibc手册中建议的那样:gnu.org/software/libc/manual/html_node/Elapsed-Time.html
Peter Cordes

3

ANSI C仅指定秒精度时间函数。但是,如果您在POSIX环境中运行,则可以使用gettimeofday()函数,该函数提供自UNIX时代以来经过的时间的微秒分辨率。

附带说明一下,我不建议使用clock(),因为Clock()在许多(如果不是全部?)系统上实现不好,而且不准确,除了它仅指程序在CPU和CPU上花费了多长时间的事实。而不是程序的总寿命,根据您的问题,这是我假设您要衡量的。


ISO C标准(假设这是ANSI C的意思)故意未指定时间函数的精度。然后,特别是在POSIX实现或Windows上,挂钟(请参阅Thomas的回答)的精度以秒为单位。但时钟()的精度通常是更大的,始终在Posix的1μS(独立的准确性。)
AntoineL

2

每个解决方案都无法在我的系统中正常工作。

我可以使用

#include <time.h>

double difftime(time_t time1, time_t time0);

2
这将两个time_t值之间的差值加倍。由于time_t值仅精确到一秒,因此在打印短时间运行的程序所花费的时间方面价值有限,尽管它对于长时间运行的程序可能很有用。
乔纳森·莱夫勒

不管出于什么原因,传入一个clock_ts difftime对我来说都可以达到百分之一秒的精度。这是在Linux x86上。我也不能得到的减法stopstart工作。
ragerdl

@ragerdl:您需要传递给difftime() clock() / CLOCKS_PER_SEC,因为它需要几秒钟。
ALK

2
    #include<time.h>
    #include<stdio.h>
    int main(){
clock_t begin=clock();

    int i;
for(i=0;i<100000;i++){
printf("%d",i);

}
clock_t end=clock();
printf("Time taken:%lf",(double)(end-begin)/CLOCKS_PER_SEC);
}

该程序将像魅力一样工作。


2

我发现,每个人都在这里建议使用通常的clock(),由于某种原因,即使是对于没有任何副作用(例如绘制到屏幕或读取文件)的静态代码,运行之间也会有很大的出入。这可能是因为CPU更改了功耗模式,操作系统赋予了不同的优先级等...

因此,每次使用clock()可靠地获得相同结果的唯一方法是多次(在几分钟内)在循环中运行被测代码,并采取预防措施以防止编译器对其进行优化:现代编译器可以预计算代码没有副作用循环运行,然后将其移出循环,例如每次迭代都使用随机输入。

在将足够多的样本收集到一个数组中之后,对数组进行排序,然后获取中间元素,称为中值。中位数比平均水平要好,因为它可以消除极端偏差,例如说杀毒软件占用了所有CPU或操作系统进行了一些更新。

这是一个用于测量C / C ++代码的执行性能的简单实用程序,可以对中值附近的值进行平均:https : //github.com/saniv/gauge

我自己仍在寻找一种更健壮和更快的方式来测量代码。人们可能会尝试在没有任何操作系统的裸机上在受控条件下运行代码,但这会产生不切实际的结果,因为实际上OS确实参与其中。

x86具有这些硬件性能计数器,其中包括实际执行的指令数量,但是在没有OS帮助的情况下它们很难访问,难以解释并且有自己的问题(http://archive.gamedev.net/archive/reference/articles /article213.html)。他们仍然可能有助于调查瓶颈的性质(数据访问或对该数据的实际计算)。


是的,现代x86 CPU的空闲速度比max turbo慢得多。根据“总督”设置,提高到最大时钟速度可能需要一毫秒(带有硬件P状态管理的Skylake,尤其是将energy_performance_preference设置为performance)或数十毫秒。 en.wikipedia.org/wiki/Dynamic_frequency_scaling。是的,中等性能通常是一个不错的选择。高端通常会受到干扰的影响。
彼得·科德斯

避免工作优化的最佳选择通常是命令行输入并返回结果。或在一个单独的文件中编写一个函数,该函数main使用arg并返回结果,并且不使用链接时优化。然后,编译器无法将其内联到调用程序中。仅在函数已经包含某种循环的情况下才起作用,否则调用/重载开销太大。
彼得·科德斯

如果您使用静态代码处理单个命令行输入而没有任何副作用,则编译器仍可以优化循环外的单个命令行输入。因此,最好为每次迭代生成一个随机输入。显然,应该在第一个clock()之前在测量的代码之外调用rand(),因为rand()也可能导致系统调用,对一些硬件熵生成器进行采样(在较旧的系统上是鼠标移动)。只是不要忘记将输出的每一位都打印出来,否则编译器可能会决定您不需要全部或部分输出。可以说CRC32。
SmugLispWeenie

如果要测试的代码放在一个单独的文件中,并且您不使用链接时优化,则编译器无法执行CSE来优化两次调用之间的关系。呼叫者不能假设没有任何明显的副作用。这使您可以重复循环中放入相对较短的内容,使其足够长的时间,而仅包含调用/重载开销。如果您将其内联,则必须检查生成的asm,以确保它不会像您所说的那样使计算中断。
彼得·科德斯

特定于编译器的方式是(例如)使用GNU C内联汇编来强制编译器在寄存器中实现结果,和/或忘记它对变量值的了解,而无需实际引入额外的指令。 MSVC中的“ Escape”和“ Clobber”等价物链接到有关概要分析和微基准测试的视频(Clang开发人员Chandler Carruth的CppCon 2015演讲),没有MSVC的等价物,但问题本身显示了GNU C函数以及如何使用它们。
彼得·科德斯

0

有些人可能会发现不同类型的输入有用的:我得到了测量时间作为GPGPU编程与NVIDIA CUDA(大学课程的一部分,这种方法课程描述)。它结合了先前文章中看到的方法,我之所以简单地发布它是因为要求赋予了它可信性:

unsigned long int elapsed;
struct timeval t_start, t_end, t_diff;
gettimeofday(&t_start, NULL);

// perform computations ...

gettimeofday(&t_end, NULL);
timeval_subtract(&t_diff, &t_end, &t_start);
elapsed = (t_diff.tv_sec*1e6 + t_diff.tv_usec);
printf("GPU version runs in: %lu microsecs\n", elapsed);

我想您可以乘以例如1.0 / 1000.0以获得适合您需要的度量单位。


1
gettimeofday已过时,不建议使用。clock_gettime相反,建议使用其POSIX手册页,它可以让您要求CLOCK_MONOTONIC不受系统时钟更改的影响,因此,最好使用间隔计时器。例如,在现代Linux系统上,gettimeofday基本上是clock_gettime将纳秒转换为微秒的包装器。(请参阅JohnSll的答案)。
彼得·科德斯

该方法由@Wes Hardaker添加,主要区别是使用timeval_subtract
ワイきんぐ

好的,答案中唯一有用的部分是您未定义的函数名称,该名称不在标准库中。(仅在glibc手册中:gnu.org/software/libc/manual/html_node/Elapsed-Time.html)。
彼得·科德斯

-2

比较气泡排序和选择排序的执行时间我有一个程序比较气泡排序和选择排序的执行时间。要找出执行代码块的时间,请通过以下方式计算代码块之前和之后的时间:

 clock_t start=clock();
 
 clock_t end=clock();
 CLOCKS_PER_SEC is constant in time.h library

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
   int a[10000],i,j,min,temp;
   for(i=0;i<10000;i++)
   {
      a[i]=rand()%10000;
   }
   //The bubble Sort
   clock_t start,end;
   start=clock();
   for(i=0;i<10000;i++)
   {
     for(j=i+1;j<10000;j++)
     {
       if(a[i]>a[j])
       {
         int temp=a[i];
         a[i]=a[j];
         a[j]=temp;
       }
     }
   }
   end=clock();
   double extime=(double) (end-start)/CLOCKS_PER_SEC;
   printf("\n\tExecution time for the bubble sort is %f seconds\n ",extime);

   for(i=0;i<10000;i++)
   {
     a[i]=rand()%10000;
   }
   clock_t start1,end1;
   start1=clock();
   // The Selection Sort
   for(i=0;i<10000;i++)
   {
     min=i;
     for(j=i+1;j<10000;j++)
     {
       if(a[min]>a[j])
       {
         min=j;
       }
     }
     temp=a[min];
     a[min]=a[i];
     a[i]=temp;
   }
   end1=clock();
   double extime1=(double) (end1-start1)/CLOCKS_PER_SEC;
   printf("\n");
   printf("\tExecution time for the selection sort is %f seconds\n\n", extime1);
   if(extime1<extime)
     printf("\tSelection sort is faster than Bubble sort by %f seconds\n\n", extime - extime1);
   else if(extime1>extime)
     printf("\tBubble sort is faster than Selection sort by %f seconds\n\n", extime1 - extime);
   else
     printf("\tBoth algorithms have the same execution time\n\n");
}

4
adimoh答案相比,这实际上并没有添加任何新内容,只是它用一些实际代码填充了“可执行代码”块(或其中的两个)。而这个问题的答案不添加任何东西,这不是在亚历山大ç答案从两个年度的同期。
乔纳森·莱夫勒
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.