移动构造函数和赋值中的存储和加载指令数
Number of store and load instructions in move constructor and assignment
答案 here 说,对于任何具有一个拥有指针的对象。
Move constructor: 1 load and 2 stores.
Move assignment: 2 loads and 2 stores.
我的问题是,这是如何工作的。我的想法
移动作业
MyClass& operator=(MyClass&& rhs);//lhs is this
首先必须加载 lhs
和 rhs
。然后 rhs
存储到 lhs
。这占两个负载和一个商店。 rhs
是否仍被存储回来,即使没有任何更改并且它是一个右值?
移动构造函数
MyClass(MyClass&& rhs);
这次只rhs
加载到寄存器中。然后存储到lhs
。即一载一存。比什么?同样,如上所述,我错过了一家商店。
正如针对具有单个拥有指针的 class 的优化案例的链接答案中所指出的:
移动构造函数
MyClass(MyClass&& rhs):
ptr(rhs.ptr) { // load rhs.ptr, store it in ptr
rhs.ptr = nullptr; // store nullptr in rhs.ptr
}
- 加载rhs指针值,
- 将其存储在新对象中并
- 在rhs对象中存储
nullptr
移动构造函数不需要加载当前指针,因为它通常是nullptr
。
移动作业
MyClass& operator=(MyClass&& rhs) {
auto r3 = this->ptr; // load this->ptr
this->ptr = rhs.ptr; // load rhs.ptr, store it in ptr
rhs.ptr = r3; // store rhs.ptr
return *this;
}
- 加载当前指针值,
- 加载rhs指针值,
- 在当前对象中存储 rhs 并且
- 将第 1 步中的旧值存储在 rhs 移出对象中,从而转移所有权并确保将其删除。
所以移动分配看起来就像交换。没有自分配检查以防止分支。
赋值必须确保指针的前一个值指向的内存被释放并且源对象处于有效状态。确保这一点的最简单方法是将旧指针存储在 rhs 对象中,它将删除它。
这里可以看到clang 5.0.0(https://godbolt.org/g/WxGxZ4)生成的稍微优化过的代码:
MyClass::MyClass(MyClass&&): # @MyClass::MyClass(MyClass&&)
mov rax, qword ptr [rsi]
mov qword ptr [rdi], rax
mov qword ptr [rsi], 0
ret
MyClass::operator=(MyClass&&): # @MyClass::operator=(MyClass&&)
mov rax, qword ptr [rdi]
mov rcx, qword ptr [rsi]
mov qword ptr [rdi], rcx
mov qword ptr [rsi], rax
mov rax, rdi
ret
答案 here 说,对于任何具有一个拥有指针的对象。
Move constructor: 1 load and 2 stores.
Move assignment: 2 loads and 2 stores.
我的问题是,这是如何工作的。我的想法
移动作业
MyClass& operator=(MyClass&& rhs);//lhs is this
首先必须加载 lhs
和 rhs
。然后 rhs
存储到 lhs
。这占两个负载和一个商店。 rhs
是否仍被存储回来,即使没有任何更改并且它是一个右值?
移动构造函数
MyClass(MyClass&& rhs);
这次只rhs
加载到寄存器中。然后存储到lhs
。即一载一存。比什么?同样,如上所述,我错过了一家商店。
正如针对具有单个拥有指针的 class 的优化案例的链接答案中所指出的:
移动构造函数
MyClass(MyClass&& rhs):
ptr(rhs.ptr) { // load rhs.ptr, store it in ptr
rhs.ptr = nullptr; // store nullptr in rhs.ptr
}
- 加载rhs指针值,
- 将其存储在新对象中并
- 在rhs对象中存储
nullptr
移动构造函数不需要加载当前指针,因为它通常是nullptr
。
移动作业
MyClass& operator=(MyClass&& rhs) {
auto r3 = this->ptr; // load this->ptr
this->ptr = rhs.ptr; // load rhs.ptr, store it in ptr
rhs.ptr = r3; // store rhs.ptr
return *this;
}
- 加载当前指针值,
- 加载rhs指针值,
- 在当前对象中存储 rhs 并且
- 将第 1 步中的旧值存储在 rhs 移出对象中,从而转移所有权并确保将其删除。
所以移动分配看起来就像交换。没有自分配检查以防止分支。
赋值必须确保指针的前一个值指向的内存被释放并且源对象处于有效状态。确保这一点的最简单方法是将旧指针存储在 rhs 对象中,它将删除它。
这里可以看到clang 5.0.0(https://godbolt.org/g/WxGxZ4)生成的稍微优化过的代码:
MyClass::MyClass(MyClass&&): # @MyClass::MyClass(MyClass&&)
mov rax, qword ptr [rsi]
mov qword ptr [rdi], rax
mov qword ptr [rsi], 0
ret
MyClass::operator=(MyClass&&): # @MyClass::operator=(MyClass&&)
mov rax, qword ptr [rdi]
mov rcx, qword ptr [rsi]
mov qword ptr [rdi], rcx
mov qword ptr [rsi], rax
mov rax, rdi
ret