Administrator
发布于 2026-04-02 / 11 阅读
0
0

如何确保程序被精确的测量?


一、核心问题在哪里?

for (int i = 0; i < N; i++)
    clock();

编译器可能做的事:

1. 死代码消除(DCE)

如果 clock() 的返回值没用:

clock(); // 返回值丢弃

👉 编译器可能认为:

“这个调用没有副作用,可以删掉”

⚠️ 虽然标准上 clock() 有副作用(读取时间),但编译器不一定保守


2. 循环优化 / 合并

可能变成:

clock(); // 只调用一次

或者:

  • loop unrolling

  • loop invariant code motion


3. 内联(Inlining)

如果 libc 实现允许:

clock()

可能被展开为:

  • syscall

  • vDSO

  • 甚至 rdtsc

👉 导致你测的不是“函数调用成本”


4. CPU 层面问题

即使编译器没优化:

  • out-of-order execution

  • instruction reordering

  • cache / branch predictor 热身

👉 会影响 timing


二、如何“反优化”(关键手段)

✅ 方法1:使用 volatile

强制编译器认为结果“有用”

volatile clock_t sink;

for (int i = 0; i < N; i++)
    sink = clock();

👉 防止 DCE


✅ 方法2:禁止内联

GCC / Clang:

__attribute__((noinline))
clock_t myclock() {
    return clock();
}

然后:

sink = myclock();

👉 防止函数被 inline


✅ 方法3:加入编译屏障(compiler barrier)

asm volatile("" ::: "memory");

用法:

for (int i = 0; i < N; i++) {
    asm volatile("" ::: "memory");
    sink = clock();
}

👉 防止编译器重排


✅ 方法4:防止循环被优化掉

for (int i = 0; i < N; i++) {
    sink ^= clock();
}

👉 引入数据依赖(dependency chain)


✅ 方法5:使用 -fno-builtin

编译:

gcc -O2 -fno-builtin

👉 防止编译器把 clock() 当成内建函数优化


✅ 方法6:关闭部分优化(可选)

-O2 -fno-tree-vectorize -fno-unroll-loops

👉 避免:

  • loop unrolling

  • vectorization


三、更“硬核”的方法(防 CPU 层面影响)

✅ 序列化指令(x86)

如果你想极致精确:

#include <x86intrin.h>

unsigned long long start = __rdtscp(&aux);
// code
unsigned long long end = __rdtscp(&aux);

或:

_cpuid();
rdtsc();

👉 防止乱序执行


四、你的代码改进版本(推荐)

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

const int N = 1000000;

volatile clock_t sink;

__attribute__((noinline))
clock_t myclock() {
    return clock();
}

int main() {
    clock_t start = clock();

    for (int i = 0; i < N; i++) {
        asm volatile("" ::: "memory"); // compiler barrier
        sink ^= myclock();
    }

    clock_t end = clock();

    double duration = (double)(end - start) / CLOCKS_PER_SEC;
    printf("%.2f ns per iteration\n", 1e9 * duration / N);

    return 0;
}

五、但还有一个更大的问题(很多人忽略)

👉 你测的不是 clock() 本身,而是:

clock() + loop + branch + pipeline + measurement noise

尤其:

clock() 分辨率很低

在很多系统上:

  • 精度 = 毫秒级

  • 甚至更差

👉 测 ns 级别完全不靠谱


六、更正确的方式

✅ 用 clock_gettime

#include <time.h>

clock_gettime(CLOCK_MONOTONIC, &ts);

👉 纳秒级精度


✅ 或直接测 TSC(最底层)

__rdtsc()

👉 cycle 精度


七、终极建议(非常重要)

如果你真的想做严肃 benchmark:

👉 用:

  • Google Benchmark

  • perf

  • rdtsc + pin CPU


总结一句话

要避免 benchmark 被“优化掉”,你必须同时对抗:编译器优化(DCE / inline / 重排)和 CPU 行为(乱序 / cache / pipeline),否则测出来的结果很可能是假的。


如果你愿意,我可以帮你进一步分析:
👉 Linux 下 clock() 实际是 syscall 还是 vDSO(性能差异巨大)
这个会直接影响你测出来是“几十 ns”还是“几百 ns”。


评论