铿锵声中的静态预计算优化

我有

#include  #include  #include  #include  #include  int fib(int n) { return n < 2 ? n : fib(n-1) + fib(n-2); } double clock_now() { struct timeval now; gettimeofday(&now, NULL); return (double)now.tv_sec + (double)now.tv_usec/1.0e6; } #define NITER 5 

在我的main() ,我正在做一个像这样的简单基准:

 printf("hi\n"); double t = clock_now(); int f = 0; double tmin = INFINITY; for (int i=0; i<NITER; ++i) { printf("run %i, %f\n", i, clock_now()-t); t = clock_now(); f += fib(40); t = clock_now()-t; printf("%i %f\n", f, t); if (t < tmin) tmin = t; t = clock_now(); } printf("fib,%.6f\n", tmin*1000); 

当我使用clang -O3 (来自Xcode 5.0.1的LLVM 5.0)编译时,它总是打印出零时间,除了在for -loop的init之外,即:

 hi run 0, 0.866536 102334155 0.000000 run 1, 0.000001 204668310 0.000000 run 2, 0.000000 307002465 0.000000 run 3, 0.000000 409336620 0.000000 run 4, 0.000001 511670775 0.000000 fib,0.000000 

它似乎静态预先计算了fib(40)并将其存储在某处。 对? 开始时的奇怪延迟(0.8秒)可能是因为它加载了缓存?

我这样做是为了进行基准测试。 C编译器应该尽可能地优化fib()本身。 但是,我不希望它在编译时预先计算它。 所以基本上我希望所有代码尽可能地优化,但不是main() (或者至少不是这种特定的优化)。 我能以某种方式这样做吗?

在这种特定情况下,它还有什么优化? 这有点奇怪而且相当不错。

我通过标记某些数据volatile找到解决方案。 Esp,我做的是:

 volatile int f = 0; //... volatile int FibArg = 40; f += fib(FibArg); 

这样,它会强制编译器在调用函数时读取FibArg ,并强制它不要假定它是常量。 因此,它必须调用函数来计算它。

目前我的编译器不需要volatile int f ,但是当编译器发现fib没有副作用并且其结果和f都被使用时,它可能在将来。


请注意,这仍然不是结束。 到目前为止,未来的编译器可能已经进步,它猜测40可能是fib参数。 也许它为可能的值构建了一个数据库。 对于最可能的值,它会构建一个小缓存。 当调用fib时,它会快速运行时检查它是否缓存了该值。 当然,运行时检查会增加一些开销,但是编译器估计这个开销对于某些特定代码来说是微不足道的,这与高速缓存获得的速度有关。

我不确定编译器是否会进行这样的优化,但它可以。 Profile Guided Optimization(PGO)已经朝这个方向发展。