请考虑以下主要为基准测试而创建的类:
class String {
char* data_;
public:
String(const char* arg = "") : data_(new char[strlen(arg) + 1]) { strcpy(data_, arg); }
String(const String& other) : String(other.data_) { }
String(String&& other) noexcept : data_(other.data_) { other.data_ = nullptr; }
String& operator=(String other) noexcept { swap(other); return *this; }
~String() { delete[] data_; }
void swap(String& rhs) noexcept { std::swap(data_, rhs.data_); }
const char* data() const { return data_; }
};
void swap(String& lhs, String& rhs) noexcept { lhs.swap(rhs); }
我正在尝试将两个实例的交换效率与自定义
swap
和
std::swap
. 用于自定义
掉期
,合同一般条件8.2(
-O2
)生成以下x86_64程序集:
mov rax, QWORD PTR [rdi]
mov rdx, QWORD PTR [rsi]
mov QWORD PTR [rdi], rdx
mov QWORD PTR [rsi], rax
ret
与两个指针的交换完全匹配。然而,对于
STD::掉期
,生成的程序集为:
mov rdx, QWORD PTR [rsi]
mov QWORD PTR [rsi], 0
mov rax, QWORD PTR [rdi]
mov QWORD PTR [rdi], 0
mov QWORD PTR [rsi], rax
mov rax, QWORD PTR [rdi]
mov QWORD PTR [rdi], rdx
test rax, rax
je .L3
mov rdi, rax
jmp operator delete[](void*)
.L3:
ret
我好奇的是,gcc为什么会生成如此低效的代码。指令(1)设置
[rdi]
归零。然后将0加载到
rax
(2)。然后,
拉克斯
是否测试(3)
operator delete
应该被叫来。
为什么GCC测试
拉克斯
如果保证为零?对于优化器来说,避免这个测试似乎是一个非常简单的例子。
Godbolt演示:
https://godbolt.org/z/WNm2if
效率低下的另一个原因是0被写入
[rsi]
首先(a),然后用另一个值(b)覆盖它。
底线:我希望编译器为
STD::掉期
以及风俗习惯
掉期
,但这不会发生。这表明,即使对于支持移动语义的类,编写自定义交换函数也是有意义的。