一、核心问题在哪里?
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”。