【问题标题】:Measuring amount of CPU time taken by a piece of code, in C on Unix/Linux在 Unix/Linux 上用 C 语言测量一段代码占用的 CPU 时间
【发布时间】:2010-01-11 06:22:18
【问题描述】:

clock() 是否可以用作可靠的 API 来测量 CPU 执行 sn-p 代码所花费的时间?验证使用时间()/时钟()时,两者似乎都不能精确测量 CPU 时间。

首先,API 的clock()/times() 是否可以用于测量函数/sn-p 代码的执行时间,如下例所示?有没有更好更可靠的选择?该机制适用于 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 的巨大结构,分配此结构的副本,Oracle DB 选择并使用来自 DB 的数据填充这些结构]措施。即使这显示为 0 滴答声,这也是让我感到困惑的原因。

谢谢!

【问题讨论】:

  • 谷歌搜索“c profiler”,这是您正在寻找的工具的名称..
  • 对于小块,您的编译器可能会优化相同缓冲区的重复清除,并且只清除一次。

标签: c unix performance


【解决方案1】:

在最近的 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 更容易。但是,如果您想准备在每个线程的基础上执行此操作,那么这种技术比文件名更好,代码对于每个进程或每个线程获取数据都是相同的。

* - 我不确定引入统计文件的确切时间。您需要验证您的系统是否有它。

【讨论】:

  • BLTPR_USEC_PER_SEC 的define(可能是#define)是不是要在后面的代码中使用?
【解决方案2】:

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

还可以与gettimeofday 核对以与挂钟时间进行比较。

【讨论】:

  • 谢谢! getrusage() 听起来很有希望。但是 gettimeofday() 是值得怀疑的。我需要测量仅用于我的进程的 CPU 时间。由于需要考虑变化的系统负载,因此测量挂钟时间似乎不是正确的方法。如有错误请指正。
  • 你说得对,挂钟不适合测量你的应用程序,但有时它可能是一种可能有用的快速而肮脏的测量方法。
  • 我建议使用 gettimeofday 进行统计(仅用于比较)。
【解决方案3】:

我会尝试将时间与 shell 的 time 命令关联起来,作为健全性检查。

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

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

人们认为 600 nS 会四舍五入到 1 个时钟滴答,但我也会担心这一点。

【讨论】:

  • memset 应该在大多数当前架构上使用 16 字节存储。
  • 谢谢你,DigitalRoss。但是上面的 sn -p 只是一个例子。我实际上正在尝试测量一个准备一个巨大结构(大小约为 1MB)的函数,mallocs 该结构的副本,Oracle db 是否选择并使用来自 DB 的数据填充这些结构。我注意到,即使使用时钟()测量,它也显示为 0 个滴答声。一个通过网络执行 i/o 并且仍然显示为 0 滴答声的函数让我感到困惑。 :(
【解决方案4】:

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

或者你可以使用 linux time 命令。例如:时间 [程序] [参数]

【讨论】:

  • >>使用clock_t 来获取程序启动后的CPU 滴答数 那么是否应该将结果解释为“memset() 100 KB 块花费了 0 个 CPU 滴答,500 次”?我有点困惑。 :( >>time 命令它会测量整个程序所花费的时间,而不是特定的函数/代码段,不是吗?
  • 时间计算用户、真实和系统时间。这些测量可能很有用。但是,是的,它适用于整个程序。
【解决方案5】:

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

必须确认这是否真的可以解决。

在 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 >

【讨论】:

    【解决方案6】:

    操作系统仅定期更新进程/线程的资源使用情况。代码 sn-p 完全有可能在下一次更新之前完成,从而产生零资源使用差异。不能说有关 HP 或 AIX 的任何信息,请参阅 Solaris Performance and Tools Sun 的书。对于 Linux,您想查看 oprofile 和更新的 perf tool。在分析方面valgrind 会有很大帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-23
      • 2013-06-30
      • 2013-12-08
      • 2015-05-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多