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

时间:2010-01-11 06:22:19

标签: c unix performance

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

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

请建议。另外,如果我遗漏了任何小事,请告诉我。

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}\n",
        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滴答,这也让我感到困惑。

谢谢!

6 个答案:

答案 0 :(得分:7)

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

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

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

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文件的确切时间。您需要验证您的系统是否已使用它。

答案 1 :(得分:5)

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

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

答案 2 :(得分:4)

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

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

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

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

答案 3 :(得分:1)

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

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

答案 4 :(得分:0)

有关高分辨率计时器的一些信息here on HP's page。此外,http://www.fftw.org/cycle.h中也使用了相同的技巧_Asm_mov_from_ar (_AREG_ITC);

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

示例prog,在HP-UX 11.31上测试:

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]\n",
                          elapsed(t2,t1),elapsed(t4,t3),elapsed(t6,t5));
        free(sbuf); return 0;
}
bbb@m_001/tmp/prof >

答案 5 :(得分:0)

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