【问题标题】:Measuring processor ticks in C在 C 中测量处理器滴答声
【发布时间】:2015-10-07 22:48:24
【问题描述】:

我想计算在函数内执行相同代码时的执行时间差异。然而,令我惊讶的是,当我使用clock()/clock_t 作为启动和停止计时器时,有时时钟差为0。这是否意味着clock()/clock_t 实际上并没有返回处理器在任务上花费的点击次数

经过一番搜索,在我看来,clock_gettime() 会返回更细粒度的结果。确实如此,但我最终得到了任意数量的纳(?)秒。它暗示了执行时间的差异,但很难准确说明它究竟有多少点击差异。我该怎么做才能找到这个?

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

#define M_PI_DOUBLE (M_PI * 2)

void rotatetest(const float *x, const float *c, float *result) {
    float rotationfraction = *x / *c;
    *result = M_PI_DOUBLE * rotationfraction;
}

int main() {

    int i;
    long test_total = 0;
    int test_count = 1000000;
    struct timespec test_time_begin;
    struct timespec test_time_end;

    float r = 50.f;
    float c = 2 * M_PI * r;
    float x = 3.f;
    float result_inline = 0.f;
    float result_function = 0.f;

    for (i = 0; i < test_count; i++) {
        clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &test_time_begin);
        float rotationfraction = x / c;
        result_inline = M_PI_DOUBLE * rotationfraction;
        clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &test_time_end);
        test_total += test_time_end.tv_nsec - test_time_begin.tv_nsec;
    }

    printf("Inline clocks %li, avg %f (result is %f)\n", test_total, test_total / (float)test_count,result_inline);

    for (i = 0; i < test_count; i++) {
        clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &test_time_begin);
        rotatetest(&x, &c, &result_function);
        clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &test_time_end);
        test_total += test_time_end.tv_nsec - test_time_begin.tv_nsec;
    }

    printf("Function clocks %li, avg %f (result is %f)\n", test_total, test_total / (float)test_count, result_inline);

    return 0;
}

我在 Linux 3.13.0-37-generic(Linux Mint 16)上使用 gcc 版本 4.8.4

【问题讨论】:

  • 您需要告诉我们您正在使用哪个操作系统/哪个编译器(GCC/Linux?)。
  • 几纳秒的差异并不重要,也许您可​​以多次调用您的函数(100 万次)并测量时间差异。
  • 我想我实际上已经这样做了,@BrunoLevy。不过,我添加了详细信息,谢谢。
  • 我不够清楚,我的意思是将调用移到循环之外的clock_gettime()。
  • 您使用了哪些编译器选项?如果你使用了 -O,编译器可能决定内联函数调用(这可以解释为什么你得到相同的时间)。

标签: c benchmarking clock


【解决方案1】:

在谷歌上搜索了一下之后,我可以看到 clock() 函数可以用作标准机制来查找要执行的书,但请注意,根据处理器的负载,时间会在不同的时间发生变化, 您可以使用以下代码进行计算

clock_t begin, end;
double time_spent;

begin = clock();
/* here, do your time-consuming job */
end = clock();
time_spent = (double)(end - begin) / CLOCKS_PER_SEC;

【讨论】:

    【解决方案2】:

    首先:正如在 cmets 中已经提到的那样,一个接一个地执行一次运行可能对您没有好处。如果一切都走下坡路,那么获取时间的调用实际上可能需要比实际执行操作更长的时间。

    请记录操作的多次运行(包括预热阶段,以便交换所有内容)并计算平均运行时间。

    clock() 不保证是单调的。它也不是程序已运行的处理器点击次数(无论您将其定义为什么)。描述来自clock() 的结果的最佳方式可能是“尽最大努力估计任何一个 CPU 用于计算当前进程的时间”。因此,对于基准测试而言,clock() 几乎是无用的。

    作为per specification:

    clock() 函数返回实现对进程使用的处理器时间的最佳近似值,因为该实现依赖于仅与进程调用相关的时间开始。

    另外

    要确定以秒为单位的时间,clock() 返回的值应除以宏 CLOCKS_PER_SEC 的值。

    所以,如果你打电话给clock() 的次数超过了解决办法,那你就倒霉了。

    对于分析/基准测试,您应该(如果可能)使用现代硬件上可用的性能时钟之一。主要候选人可能是

    编辑:问题现在引用CLOCK_PROCESS_CPUTIME_ID,这是 Linux 公开 TSC 的方式。

    是否有任何(或两者)可用取决于硬件也是操作系统特定的。

    【讨论】:

    • 我假设一个处理器单击将是例如执行汇编指令“inc EAX”所需的时间,但根据您在上面的指示,我认为这不是那么容易吗?你建议我如何通过我的 clock_gettime() 和 CLOCK_PROCESS_CPUTIME_ID 检查 TSC 是否可用?
    • 在现代 CISC 处理器上,执行诸如 inc EAX 之类的操作可能需要多次“处理器点击”,并且它可以并行执行许多 inc EAX 指令,因此根据发生的其他情况,结果会有所不同你运行它的时间。
    • @lash: clock_getcpuclockid(pid, CLOCK_PROCESS_CPUTIME_ID) 在时钟可用时返回非零值。我也不太确定滴答计数器在现代多核环境中是否真的有用。 Linux 联机帮助页甚至警告说,在多 CPU 环境中,时钟值可能是虚假的。此外,现代(尤其是多核)CPU 执行乱序调度并具有动态指令延迟,因此 inc %eax 需要一个滴答声 不再有效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多