返回一个私有类成员比使用结构并直接访问该变量更慢?

时间:2014-04-17 06:03:28

标签: c++ performance class struct

假设您有一个具有私有成员的类,这些成员在程序中被大量访问(例如在一个必须快速的循环中)。想象一下,我已经定义了这样的东西:

class Foo
{
public: 

    Foo(unsigned set)
        : vari(set)
    {}

    const unsigned& read_vari() const { return vari; }

private:
    unsigned vari;
};

我想这样做的原因是,一旦创建了类,“vari”就不应再被更改了。因此,为了最大限度地减少错误发生,“当时似乎是一个好主意”。

但是,如果我现在需要数百万次调用此函数,我想知道是否存在开销和减速而不是简单地使用:

struct Foo
{
    unsigned vari;
};

那么,这是我在使用类时的第一个影响因素,以避免任何人在构造函数设置后错误地更改变量的值? 此外,这是否以函数调用开销的形式引入“惩罚”。 (假设我在编译器中使用优化标志,例如GCC中的-O2)?

4 个答案:

答案 0 :(得分:9)

它们应该是一样的。请记住,您尝试在向量上使用operator[]并且gdb刚刚回复optimized out时遇到了令人沮丧的时间?这就是这里会发生的事情。编译器不会在这里创建函数调用,而是直接访问变量。

我们来看看下面的代码

struct foo{
   int x;
   int& get_x(){
     return x;
   }   
};

int direct(foo& f){ 
   return  f.x;
}

int fnc(foo& f){ 
   return  f.get_x();
}

使用g++ test.cpp -o test.s -S -O2编译的。 -S标志告诉编译器“在编译阶段之后停止;不要汇编(从g ++手册页引用)”。这就是编译器给我们的:

_Z6directR3foo:
.LFB1026:
  .cfi_startproc
  movl  (%rdi), %eax
  ret 

_Z3fncR3foo:
.LFB1027:
  .cfi_startproc
  movl  (%rdi), %eax
  ret 

如您所见,在第二种情况下没有进行任何函数调用,它们都是相同的。这意味着使用访问器方法没有性能开销。

奖励:如果关闭优化会怎样?相同的代码,结果如下:

_Z6directR3foo:
.LFB1022:
  .cfi_startproc
  pushq %rbp
  .cfi_def_cfa_offset 16
  .cfi_offset 6, -16
  movq  %rsp, %rbp
  .cfi_def_cfa_register 6
  movq  %rdi, -8(%rbp)
  movq  -8(%rbp), %rax
  movl  (%rax), %eax
  popq  %rbp
  .cfi_def_cfa 7, 8
  ret

_Z3fncR3foo:
.LFB1023:
  .cfi_startproc
  pushq %rbp
  .cfi_def_cfa_offset 16
  .cfi_offset 6, -16 
  movq  %rsp, %rbp
  .cfi_def_cfa_register 6
  subq  $16, %rsp
  movq  %rdi, -8(%rbp)
  movq  -8(%rbp), %rax
  movq  %rax, %rdi
  call  _ZN3foo5get_xEv    #<<<call to foo.get_x()
  movl  (%rax), %eax
  leave
  .cfi_def_cfa 7, 8
  ret

正如您所看到的那样,没有优化,sturct 比访问者更快,但谁发送的代码没有优化?

答案 1 :(得分:5)

您可以期待相同的性能。许多C ++类都依赖于此 - 例如,C ++ 11的list::size() const可以简单地返回数据成员。 (与vector()形成鲜明对比,其中我实现的实现计算size()作为指针数据成员对应begin()end()之间的差异,确保典型的迭代器用法是如果优化器无法确定size()在循环迭代中是恒定的,那么尽可能快地以可能较慢的索引迭代为代价。

通常没有特别的理由通过const引用返回类似unsigned的类型,无论如何都应该适合CPU寄存器,但是因为内联编译器不需要按字面意思(对于一个外联版本,可能会通过返回一个必须被解除引用的指针来实现。 (非典型的原因是允许获取变量的地址,这就是为什么说vector::operator[](size_t) const需要返回const T&而不是T,即使T很小足以装入登记册。)

答案 2 :(得分:0)

只有一种方法可以肯定地告诉您在特定程序中使用特定平台上的特定优化标记构建的特定程序中哪一个更快 - 通过测量两种变种。

话虽如此,二进制文件可能是完全相同的,教学指导。

答案 3 :(得分:0)

正如其他人所说,现在优化者依赖于抽象化(特别是在C ++中,或多或少是为了利用它而构建的)并且它们非常非常好。

但你可能不需要吸气剂。

struct Foo {
    Foo(unsigned set) : vari(set) {}
    unsigned const vari;
};

const禁止初始化。