-1

以下代码测试 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;
}
4

2 回答 2

4

检查空指针几乎可以肯定是一个时钟周期的操作。在任何现代处理器上,这将是十亿分之一秒。我认为这超出了滴答计数器的分辨率(取决于平台,它以毫秒或微秒计)。

如果您真的想知道,请查看调试器中发出的汇编代码。

于 2013-07-10T03:07:08.477 回答
0

这种情况下的测量结果-O3为 0,因此很明显,编译器优化的远不止空指针检查。它完全删除了循环,原因是循环中生成的结果从未使用过。

您确实有a.get();并且a1.get();在代码中检索循环中生成的值,但同样,这些函数调用的结果不涉及任何副作用,因此编译器将它们删除。

如果将这些调用替换为在调用之后放置get()的打印语句(以确保 IO 不包含在时间测量中),则会得到不同的结果(我已经从输出中删除了和的实际值):clock()aa1

2540000.000000 ticks (2.540000 secs)
0.000000 ticks (0.000000 secs)

所以第一个循环现在执行一些实际操作,而第二个循环仍然优化(Clang 3.3)。其原因可能是 的设置a1._a取决于一个指针,该指针被初始化0,然后在循环中永远不会改变。在将函数调用内联a1.set()到主循环后,编译器很容易看到a1._a永远不会发生 的赋值。


(注意:由于a1._a从未实际设置,打印其值意味着打印从未初始化的变量的值。确实,我得到了一个看起来非常随机的输出,当然,严格来说,打印该值会调用未定义的行为。)

于 2013-07-10T03:24:36.153 回答