我有以下代码来测试
std::string
课堂上,结果让我很惊讶,move-ctor是
~1.4
比copy ctor慢倍。
据我所知,移动构造不需要分配内存
标准::字符串
在这种情况下,move-constructed对象中可能有一个内部指针直接设置为move-object的指针,它应该比为缓冲区分配内存,然后在复制构建时从对象中复制内容更快。
代码如下:
#include <string>
#include <iostream>
void CopyContruct(const std::string &s) {
auto copy = std::string(s);
}
void MoveContruct(std::string &&s) {
auto copy = std::move(s);
}
int main(int argc, const char *argv[]) {
for (int i = 0; i < 50000000; ++i) {
CopyContruct("hello world");
}
return 0;
}
编辑:
从这两个函数的组合中,我可以看出
MoveConstruct
有一个
std::remove_reference
类模板,我认为这应该是罪魁祸首,但我不熟悉汇编,任何人都可以详细说明吗?
以下代码在上反编译
https://godbolt.org/
对于x86-64 gcc7.2:
CopyContruct(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&):
push rbp
mov rbp, rsp
sub rsp, 48
mov QWORD PTR [rbp-40], rdi
mov rdx, QWORD PTR [rbp-40]
lea rax, [rbp-32]
mov rsi, rdx
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
lea rax, [rbp-32]
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()
nop
leave
ret
MoveContruct(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&):
push rbp
mov rbp, rsp
sub rsp, 48
mov QWORD PTR [rbp-40], rdi
mov rax, QWORD PTR [rbp-40]
mov rdi, rax
call std::remove_reference<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>::type&& std::move<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)
mov rdx, rax
lea rax, [rbp-32]
mov rsi, rdx
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&)
lea rax, [rbp-32]
mov rdi, rax
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()
nop
leave
ret
编辑2:
事情变得有趣了,我改变了
标准::字符串
到
std::vector
正如@FantasticMrFox在评论中提到的,结果恰恰相反,
移动构造(MoveConstruct)
是
~1.9
时间快于
CopyConstruct
,似乎
std::remove\u引用
不是罪魁祸首,但
优化
这两类中的一类可能是。
编辑3:
以下代码是在MacOS上编译的,Apple LLVM版本为8.0.0(clang-800.0.42.1),优化标志为O3。
.section __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 11
.globl __Z12CopyContructRKNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
.align 4, 0x90
__Z12CopyContructRKNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE:
.cfi_startproc
pushq %rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
pushq %rbx
subq $24, %rsp
Ltmp3:
.cfi_offset %rbx, -24
movq %rdi, %rax
leaq -32(%rbp), %rbx
movq %rbx, %rdi
movq %rax, %rsi
callq __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_
movq %rbx, %rdi
callq __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev
addq $24, %rsp
popq %rbx
popq %rbp
retq
.cfi_endproc
.globl __Z12MoveContructONSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
.align 4, 0x90
__Z12MoveContructONSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE:
.cfi_startproc
pushq %rbp
Ltmp4:
.cfi_def_cfa_offset 16
Ltmp5:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp6:
.cfi_def_cfa_register %rbp
subq $32, %rsp
movq 16(%rdi), %rax
movq %rax, -8(%rbp)
movq (%rdi), %rax
movq 8(%rdi), %rcx
movq %rcx, -16(%rbp)
movq %rax, -24(%rbp)
movq $0, 16(%rdi)
movq $0, 8(%rdi)
movq $0, (%rdi)
leaq -24(%rbp), %rdi
callq __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev
addq $32, %rsp
popq %rbp
retq
.cfi_endproc