以下代码测试2个setter呼叫的时钟时间。一个检查空指针,一个不检查。编译的程序输出-O3标志给出相同的时间:
0.000000 ticks (0.000000 secs)
0.000000 ticks (0.000000 secs)
带有-O0标志的,时序差异仍然很小
4250000.0000 ticks (4.25000 secs)
4230000.0000 ticks (4.25000 secs)
这是由编译器在for循环中优化的吗?如果是这样,那么如何测试两个效率差异的“真实”世界情景呢?
class B {};
class A
{
public:
void set( int a ) { a_ = a; }
int get() const { return a_; }
private:
int a_;
};
class A1
public:
A1() : b_(0) {}
void set( int a ) { if( b_ ) a_ = a; }
int get() const { return a_; }
private:
int a_;
B* b_;
};
int main()
{
const int n=1000000000;
clock_t t0, t1;
A a;
A1 a1;
t0 = clock();
for( int i=0; i < n; ++i )
a.set( i );
t1 = clock();
printf( "%f ticks (%.6f secs)\n", (double) t1-t0, ((double) t1-t0) / CLOCKS_PER_SEC );
t0 = clock();
for( int i=0; i < n; ++i )
a1.set( i );
t1 = clock();
printf( "%f ticks (%.6f secs)\n", (double) t1-t0, ((double) t1-t0) / CLOCKS_PER_SEC );
return 0;
}
答案 0 :(得分:4)
检查空指针几乎肯定是一个时钟周期操作。在任何现代处理器上,这将是一个二十亿分之一秒。我认为这超出了滴答计数器的分辨率(根据平台确定毫秒或微秒)。
如果您真的想知道,请查看调试器中发出的汇编代码。
答案 1 :(得分:0)
-O3
情况下的测量结果为0,因此编译器的优化程度远远超过空指针检查。它完全删除了循环,原因是循环中生成的结果从未使用过。
代码中确实有a.get();
和a1.get();
来检索循环中生成的值,但同样,这些函数调用的结果不会涉及任何副作用,因此编译器删除它们。
如果您使用get()
调用之后放置的打印语句替换这些clock()
次调用(以确保时间测量中不包含IO),则会有所不同结果(我已从输出中删除了a
和a1
的实际值):
2540000.000000 ticks (2.540000 secs)
0.000000 ticks (0.000000 secs)
所以第一个循环现在执行一些实际操作,而第二个循环仍然被优化(Clang 3.3)。原因可能是a1._a
的设置取决于初始化为0
然后在循环中从未改变的指针。在将函数调用a1.set()
内联到主循环之后,编译器很容易看到a1._a
的赋值永远不会发生。
(注意:由于a1._a
从未实际设置,因此打印其值意味着打印一个从未初始化的变量的值。事实上,我得到一个非常随机的输出,当然,严格来说,打印该值会调用未定义的行为。)