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 是比存取速度更快,但谁船舶代码,而无需优化?
你可以期待相同的表现.许多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它足够小以适应寄存器.)
| 归档时间: |
|
| 查看次数: |
578 次 |
| 最近记录: |