使用系统单调时钟

时间:2016-12-14 23:52:04

标签: c linux macos profiling clock

是否有一种简单但可靠的方法来衡量C程序中两种算法实现之间的性能相对差异。更具体地说,我想比较实施A与B的表现?我正在考虑这样的计划:

  • 在单元测试程序中:

    1. 启动计时器
    2. 通话功能
    3. 停止计时器
    4. 在开始停止时间之间取得差异
  • 针对一对函数A和B运行上面的方案,然后获得执行时间的百分比差异以确定哪个更快。

在做了一些研究后,我遇到了关于在C中使用Monotonic clock on OSX的问题,这显然可以给我至少纳秒的精度。为了清楚起见,我理解精确的,受控制的测量很难执行,就像"With O(N) known and system clock known, can we calculate the execution time of the code?中讨论的那样,我认为在这种情况下它应该是无关紧要的,因为我只想要相对测量。

考虑到的一切,对于我想要执行的分析,这是一种充分而有效的方法吗?我可能会遗漏任何细节或考虑因素吗?

1 个答案:

答案 0 :(得分:0)

我对你所概述的时序方案进行的主要修改是确保两个函数使用相同的时序代码 - 假设它们具有相同的接口,通过将函数指针传递给骨架代码。

作为一个例子,我有一些代码可以验证一些函数是否能够验证给定的数字是否为素数。控制功能是:

static void test_primality_tester(const char *tag, int seed, int (*prime)(unsigned), int count)
{
    srand(seed);
    Clock clk;
    int nprimes = 0;
    clk_init(&clk);

    clk_start(&clk);
    for (int i = 0; i < count; i++)
    {
        if (prime(rand()))
            nprimes++;
    }
    clk_stop(&clk);

    char buffer[32];
    printf("%9s: %d primes found (out of %d) in %s s\n", tag, nprimes,
           count, clk_elapsed_us(&clk, buffer, sizeof(buffer)));
}

我非常了解srand() — why call it once?,但每次调用此函数时使用srand()一次是为了确保测试处理相同的随机数序列。在macOS上,RAND_MAX0x7FFFFFFF

类型Clock包含两个struct timespec结构的类似物,用于开始和停止时间。 clk_init()函数初始化结构; clk_start()记录结构中的开始时间; clk_stop()记录结构中的停止时间;和clk_elapsed_us()计算开始和停止时间之间的经过时间,以微秒为单位。编写软件包是为了向我提供跨平台的可移植性(代价是在确定哪个是编译时可用的最佳亚秒级时序例时)。

您可以在存储库https://github.com/jleffler/soqsrc/libsoq目录 - 文件timer.htimer.c中的Github上找到我的计时器代码。代码还没有赶上有clock_gettime()的macOS Sierra,尽管它可以编译为使用-DHAVE_CLOCK_GETTIME作为命令行编译器选项。

此代码是从函数one_test()调用的:

static void one_test(int seed)
{
    printf("Seed; %d\n", seed);
    enum { COUNT = 10000000 };
    test_primality_tester("IsPrime1", seed, IsPrime1, COUNT);
    test_primality_tester("IsPrime2", seed, IsPrime2, COUNT);
    test_primality_tester("IsPrime3", seed, IsPrime3, COUNT);
    test_primality_tester("isprime1", seed, isprime1, COUNT);
    test_primality_tester("isprime2", seed, isprime2, COUNT);
    test_primality_tester("isprime3", seed, isprime3, COUNT);
}

主程序可以使用一个或一系列种子,或者使用当前时间作为种子:

int main(int argc, char **argv)
{
    if (argc > 1)
    {
        for (int i = 1; i < argc; i++)
            one_test(atoi(argv[i]));
    }
    else
        one_test(time(0));
    return(0);
}