[英]Why does gcc generate a memmove instead of a memcpy for copying a std::vector<>?
使用gcc 5.3,以下示例中的两个函数都会生成对memmove
的调用。 生成memcpy
会不合适吗?
#include <vector>
int blackhole(const std::vector<int>&);
int copy_vec1(const std::vector<int>& v1) {
const std::vector<int> v2{v1.begin(), v1.end()};
return blackhole(v2);
}
int copy_vec2(const std::vector<int>& v1) {
const auto v2 = v1;
return blackhole(v2);
}
我尝试使用g ++ 6.1.0编译此代码。 我完全不确定细节,但我认为memmove
调用不是由编译器直接生成的; 相反,它是在实现<vector>
的代码中。
当我使用预处理代码时
/o/apps/gcc-6.1.0/bin/g++ -E -std=c++14 c.cpp
我看到两次调用__builtin_memmove
,都来自.../include/c++/6.1.0/bits/stl_algobase.h
。 看看那个头文件,我看到这个评论:
// All of these auxiliary structs serve two purposes. (1) Replace
// calls to copy with memmove whenever possible. (Memmove, not memcpy,
// because the input and output ranges are permitted to overlap.)
// (2) If we're using random access iterators, then write the loop as
// a for loop with an explicit count.
我认为发生的事情是,用于复制向量的代码更普遍适用于可以重叠的副本(例如调用std::move
(?))。
(我还没有确认汇编列表中出现的memmove
调用对应于stl_algobase.h
的__builtin_memmove
调用。我邀请其他人跟进这一点。)
根据实现, memmove()
可能会有一些相对于memcpy()
开销,但差别很小。 可能只是不值得为不能重叠的副本创建特例代码。
TL; DR GCC不优化对std::copy
内的memmove
的调用。 当使用两个C风格的数组时,它确实如此。 用*v2.data()
替换&v2[0]
允许它优化为memcpy
。
你的例子非常嘈杂,所以让我们把它剥掉:
#include <vector>
#include <algorithm>
int a[5];
int b[5];
std::vector<int> v2;
我故意将变量放在文件范围内,以防止优化它们而不必处理volatile
语义。
首先让我们试试:
std::copy(&a[0], &a[5], &b[0]);
使用-O3 -fdump-tree-optimized
这将成为:
__builtin_memcpy (&b[0], &a[0], 20);
单步执行GDB向我们展示:
Breakpoint 1, main () at test.cpp:9
9 std::copy(&a[0], &a[0] + 5, &b[0]);
(gdb) s
std::copy<int*, int*> (__result=0x601080 <b>, __last=0x6010b4, __first=0x6010a0 <a>) at test.cpp:9
9 std::copy(&a[0], &a[0] + 5, &b[0]);
(gdb) s
std::__copy_move_a2<false, int*, int*> (__result=0x601080 <b>, __last=0x6010b4, __first=0x6010a0 <a>) at test.cpp:9
9 std::copy(&a[0], &a[0] + 5, &b[0]);
(gdb) s
std::__copy_move_a<false, int*, int*> (__result=<optimized out>, __last=<optimized out>, __first=<optimized out>) at test.cpp:9
9 std::copy(&a[0], &a[0] + 5, &b[0]);
(gdb) s
std::__copy_move<false, true, std::random_access_iterator_tag>::__copy_m<int> (__result=<optimized out>, __last=<optimized out>,
__first=<optimized out>) at /usr/include/c++/5.3.1/bits/stl_algobase.h:382
382 __builtin_memmove(__result, __first, sizeof(_Tp) * _Num);
(gdb) s
main () at test.cpp:10
10 }
等等用memmove
? 好吧,让我们继续吧。
关于什么:
std::copy(&a[0], &a[5], v2.begin());
好的,让我们memmove
:
int * _2;
<bb 2>:
_2 = MEM[(int * const &)&v2];
__builtin_memmove (_2, &a[0], 20);
如果我们做-S
这反映在组件中。 单步执行GDB向我们展示了这个过程:
(gdb)
Breakpoint 1, main () at test.cpp:9
9 {
(gdb) s
10 std::copy(&a[0], &a[5], &v2[0]);
(gdb) s
std::copy<int*, int*> (__result=<optimized out>, __last=0x6010d4, __first=0x6010c0 <a>) at test.cpp:10
10 std::copy(&a[0], &a[5], &v2[0]);
(gdb) s
std::__copy_move_a2<false, int*, int*> (__result=<optimized out>, __last=0x6010d4, __first=0x6010c0 <a>) at test.cpp:10
10 std::copy(&a[0], &a[5], &v2[0]);
(gdb) s
std::__copy_move_a<false, int*, int*> (__result=<optimized out>, __last=<optimized out>, __first=<optimized out>) at test.cpp:10
10 std::copy(&a[0], &a[5], &v2[0]);
(gdb) s
std::__copy_move<false, true, std::random_access_iterator_tag>::__copy_m<int> (__result=<optimized out>, __last=<optimized out>,
__first=<optimized out>) at /usr/include/c++/5.3.1/bits/stl_algobase.h:382
382 __builtin_memmove(__result, __first, sizeof(_Tp) * _Num);
(gdb) s
__memmove_ssse3 () at ../sysdeps/x86_64/multiarch/memcpy-ssse3.S:55
啊,我明白了。 它使用C库提供的优化memcpy
例程。 但等一下,这没有意义。 memmove
和memcpy
是两回事!
查看此例程的源代码 ,我们看到几乎没有检查:
85 #ifndef USE_AS_MEMMOVE
86 cmp %dil, %sil
87 jle L(copy_backward)
88 #endif
GDB确认它将其视为一个memmove
:
55 mov %rdi, %rax
(gdb) s
61 cmp %rsi, %rdi
(gdb) s
62 jb L(copy_forward)
(gdb) s
63 je L(write_0bytes)
但是如果我们用*v2.data()
替换&v2[0]
,它就不会调用GLIBC的memmove
。 发生什么了?
好吧v2[0]
和v2.begin()
返回迭代器,而v2.data()
返回一个指向内存的直接指针。 我认为这可以防止GCC将memmove
优化为memcpy
。 [引证需要]
在这种情况下,实现者使用memmove
不是memcpy
的基本原理可能存在缺陷。
memmove
不同于memcpy
,在所述存储器区域memmove
可以重叠(并因此它的概念性略效率较低)。
memcpy
具有两个内存区域不得重叠的约束。
对于向量的复制构造函数,内存区域永远不会重叠,因此可以认为memcpy
是更好的选择。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.