眠りネ*_*ネロク 6 c++ optimization assembly gcc x86-64
考虑以下定义Foo
:
struct Foo {
uint64_t data;
};
Run Code Online (Sandbox Code Playgroud)
现在,考虑以下定义Bar
,它具有相同的数据成员Foo
,但具有一个空的 用户声明的析构函数:
struct Bar {
~Bar(){} // <-- empty user-declared dtor
uint64_t data;
};
Run Code Online (Sandbox Code Playgroud)
使用gcc 8.2 -O2
,功能copy_foo()
:
void copy_foo(const Foo* src, Foo* dst, size_t len) {
std::copy(src, src + len, dst);
}
Run Code Online (Sandbox Code Playgroud)
得到以下汇编代码:
copy_foo(Foo const*, Foo*, size_t):
salq $3, %rdx
movq %rsi, %rax
je .L1
movq %rdi, %rsi
movq %rax, %rdi
jmp memmove
.L1:
ret
Run Code Online (Sandbox Code Playgroud)
上面的汇编代码调用memmove()
以执行连续Foo
对象的副本.但是,下面的函数copy_bar()
与对象完全相同copy_foo()
,但对于Bar
对象:
void copy_bar(const Bar* src, Bar* dst, size_t len) {
std::copy(src, src + len, dst);
}
Run Code Online (Sandbox Code Playgroud)
生成以下汇编代码:
copy_bar(Bar const*, Bar*, size_t):
salq $3, %rdx
movq %rdx, %rcx
sarq $3, %rcx
testq %rdx, %rdx
jle .L4
xorl %eax, %eax
.L6:
movq (%rdi,%rax,8), %rdx
movq %rdx, (%rsi,%rax,8)
addq $1, %rax
movq %rcx, %rdx
subq %rax, %rdx
testq %rdx, %rdx
jg .L6
.L4:
ret
Run Code Online (Sandbox Code Playgroud)
此汇编代码不会调用memmove()
,而是单独执行复制.
当然,如果Bar
定义为:
struct Bar {
~Bar() = default; // defaulted dtor
uint64_t data;
};
Run Code Online (Sandbox Code Playgroud)
然后,两个函数都会产生相同的汇编代码,因为Foo
还有一个默认的析构函数.
是否有任何理由为什么用户声明类中的空析构函数会阻止编译器生成memmove()
复制该类的连续对象的调用?
std::memmove
只能用于TriviallyCopyable的对象,这需要一个简单的析构函数.普通的析构函数要求析构函数不是用户提供的.
在您的代码中Bar
:
Run Code Online (Sandbox Code Playgroud)struct Bar { ~Bar(){} // <-- empty user-declared dtor uint64_t data; };
析构函数是用户提供的,因此Bar
不是TriviallyCopyable.因此,编译器生成调用通常是不正确的std::memmove
.
通过as-if规则,编译器理论上可以检测到析构函数为空,因此等同于微不足道,但很明显,这种优化不包含在实现中std::copy
.
libstdc ++的实现std::copy
使用std::is_trivially_copyable
定义的等价物来报告,Bar
因为它不是简单的可复制的.启用此优化将要求libstdc ++具有特殊类型特征来检测此特殊情况,这通过写入可以轻易避免~Bar() = default;
当你声明自己的析构函数时,该类不再是简单的可破坏的,也不是简单的可复制的. std::memmove
要求传递给的对象是可以轻易复制的,因此不能再在类上使用它.
该标准并不要求实现检查并查看您的析构函数是否实际上是非平凡的,它只是默认为所有用户定义的析构函数都是非常重要的.
如果你的析构函数真的很小,那么就没有理由写一个.
文档std::memmove
说:
如果对象不是TriviallyCopyable,则不指定memmove的行为,并且可能未定义.
- 每个拷贝构造函数都很简单或被删除
- 每个移动构造函数都是微不足道的或删除的
- 每个复制赋值运算符都很简单或被删除
- 每个移动赋值运算符都很简单或删除至少一个复制构造函数,移动构造函数,复制赋值运算符或移动赋值运算符未被删除
- 琐碎的非删除析构函数
一个简单的析构函数需要:
- 析构函数不是用户提供的(意思是,它是隐式声明的,或者在其第一个声明中显式定义为默认值)
- 析构函数不是虚拟的(也就是说,基类析构函数不是虚拟的)
- 所有直接基类都有微不足道的析构函数
- 类类型(或类类型数组)的所有非静态数据成员都具有简单的析构函数
通过添加用户提供的析构函数,您的类型不再是可复制的并且将其传递给std::memmove
未指定或未定义的行为.
归档时间: |
|
查看次数: |
257 次 |
最近记录: |