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

Mar*_*son 4 c++ performance struct class

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

class Foo
{
public: 

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

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

private:
    unsigned vari;
};
Run Code Online (Sandbox Code Playgroud)

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

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

struct Foo
{
    unsigned vari;
};
Run Code Online (Sandbox Code Playgroud)

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

nik*_*sfi 10

他们应该是一样的.还记得那个令人沮丧的时候你试图使用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();
}
Run Code Online (Sandbox Code Playgroud)

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

_Z6directR3foo:
.LFB1026:
  .cfi_startproc
  movl  (%rdi), %eax
  ret 
Run Code Online (Sandbox Code Playgroud)

_Z3fncR3foo:
.LFB1027:
  .cfi_startproc
  movl  (%rdi), %eax
  ret 
Run Code Online (Sandbox Code Playgroud)

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

奖励:如果关闭优化会发生什么?相同的代码,结果如下:

_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
Run Code Online (Sandbox Code Playgroud)

_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
Run Code Online (Sandbox Code Playgroud)

你可以不优化看到,sturct 比存取速度更快,但谁船舶代码,而无需优化?


Ton*_*roy 5

你可以期待相同的表现.许多C++类依赖于此 - 例如,list::size() const可以期望C++ 11 可以轻松返回数据成员.(与之相反vector(),我所看到的实现计算size()为指针数据成员对应的begin()和之间的差异end(),如果优化器无法确定,则以可能较慢的索引迭代为代价确保典型的迭代器使用尽可能快这size()在循环迭代中是恒定的).

const对于类似unsigned应该适合CPU寄存器的类型,通常没有特别的理由通过引用返回,但是因为内联编译器不必逐字逐句(对于外联版本,它可能会被实现)通过返回一个必须被解除引用的指针).(非典型的原因是允许获取变量的地址,这就是为什么说vector::operator[](size_t) const需要返回a const T&而不是a T,即使T它足够小以适应寄存器.)