测量一段代码占用的CPU时间,在Unix / Linux上的C中

Measuring amount of CPU time taken by a piece of code, in C on Unix/Linux

clock()可以用作可靠的API来测量CPU执行代码片段所花费的时间吗?当使用times()/ clock()验证时,两者似乎都不能精确测量所花费的CPU时间。

首先,可以使用API?? clock()/ times()来测量函数/代码片段的执行时间,如下例所示?有没有更好更可靠的选择?该机制适用于Linux,HP-UX,IBM-AIX和Sun Solaris,因为我们需要测量(和比较)所有这些平台上的一段代码的性能。

请建议。另外,如果我遗漏了任何微不足道的事,请告诉我。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
bbb@m_001:/tmp/kk1$ ./perf_clock 102400
{clock(): S          0 E          0 D    0.0000000000}
bbb@m_001:/tmp/kk1$ ./perf_clock 204800
{clock(): S          0 E      10000 D    0.0100000000}
bbb@m_001:/tmp/kk1$ cat perf_clock.c

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

void test_clock(char* sbuf, int* len){
    clock_t start, end; int i=0;
    start = clock();
    while(i++ < 500) memset((char*)sbuf,0,*len);
    end = clock();
    printf("{clock(): S %10lu E %10lu D %15.10f}
"
,
        start,end,(end-start)/(double) CLOCKS_PER_SEC);
}
int main(int argc,char* argv[])
{
        int len=atoi(argv[1]);
        char *sbuf=(char*)malloc(len);
        test_clock(sbuf,&len);
        free(sbuf); return 0;
}

结果似乎说memset()一个100 KB的块,500次,没有时间花。还是说它在几微秒内无法测量?

实际上,它不是memset()而是另一个函数[准备一个大小约1MB的巨大结构,mallocs这个结构的副本,Oracle DB选择并使用来自DB的数据填充这些结构]我试图测量。即使这显示0滴答,这也让我感到困惑。

谢谢!


在最近的Linux(*)上。您可以从/ proc文件系统获取此信息。在文件/proc/PID/stat中,第14个条目具有用户区代码中使用的jiffies数,第15个条目具有系统代码中使用的jiffies数。

如果要基于每个线程查看数据,则应该引用文件/proc/PID/task/TID/stat

要将jiffies转换为微秒,可以使用以下命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
define USEC_PER_SEC         1000000UL

long long jiffies_to_microsecond(long long jiffies)
{
    long hz = sysconf(_SC_CLK_TCK);
    if (hz <= USEC_PER_SEC && !(USEC_PER_SEC % hz))
    {
        return (USEC_PER_SEC / hz) * jiffies;
    }
    else if (hz > USEC_PER_SEC && !(hz % USEC_PER_SEC))
    {
        return (jiffies + (hz / USEC_PER_SEC) - 1) / (hz / USEC_PER_SEC);
    }
    else
    {
        return (jiffies * USEC_PER_SEC) / hz;
    }
}

如果你关心的只是每个进程的统计信息,那么getrusage就更容易了。但是如果你想准备在每个线程的基础上做这个,这个技术比文件名更好,代码对于获取每个进程或每个线程的数据是相同的。

* - 我不确定何时引入了stat文件。您需要验证您的系统是否具有该功能。


我会尝试使用getrusage并检查系统和用户时间。

同时检查gettimeofday以与挂钟时间进行比较。


我会尝试将时间与shell的time命令相关联,作为完整性检查。

您还应该考虑编译器可能正在优化循环。由于memset不依赖于循环变量,因此编译器肯定会尝试应用称为循环不变代码运动的优化。

我还要提醒一下,10MB可能的高速缓存清除将真正是1.25或250万CPU操作,因为memset肯定以4字节或8字节的数量写入。虽然我怀疑这可以在不到一微秒的时间内完成,因为存储有点贵,而且100K增加了一些L1缓存压力,你所说的每纳秒不超过一次操作,这并不难以维持一个多GHz CPU。

人们可以想象600 nS可以完成1个时钟滴答,但我也会担心这个问题。


您可以使用clock_t来获取自程序启动以来的CPU滴答数。

或者您可以使用linux time命令。例如:time [program] [arguments]


进程/线程的资源使用仅由OS定期更新。完全有可能在下一次更新之前完成代码片段,从而产生零资源使用差异。无法说出有关HP或AIX的任何内容,请参考Sun的Solaris性能和工具书。对于Linux,您需要查看oprofile和更新的perf工具。在剖析方面,valgrind将会有很大帮助。


惠普关于高分辨率计时器页面的一些信息。此外,http://www.fftw.org/cycle.h中也使用了相同的技巧_Asm_mov_from_ar (_AREG_ITC);

必须确认这是否真的可以成为解决方案。

在HP-UX 11.31上测试的示例prog

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
bbb@m_001/tmp/prof > ./perf_ticks 1024
ticks-memset {func [1401.000000] inline [30.000000]} noop [9.000000]
bbb@m_001/tmp/prof > cat perf_ticks.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include"cycle.h" /* one from http://www.fftw.org/cycle.h */
void test_ticks(char* sbuf, int* len){
    memset((char*)sbuf,0,*len);
}
int main(int argc,char* argv[]){
        int len=atoi(argv[1]);
        char *sbuf=(char*)malloc(len);
        ticks t1,t2,t3,t4,t5,t6;
        t1 =getticks(); test_ticks(sbuf,&len); t2 =getticks();
        t3 =getticks(); memset((char*)sbuf,0,len); t4 =getticks();
        t5=getticks();;t6=getticks();
        printf("ticks-memset {func [%llf] inline [%llf]} noop [%llf]
"
,
                          elapsed(t2,t1),elapsed(t4,t3),elapsed(t6,t5));
        free(sbuf); return 0;
}
bbb@m_001/tmp/prof >