如下所示使用memcpy
是否更好?或者在性能方面使用std::copy()
会更好吗?为什么呢?
char *bits = NULL;
...
bits = new (std::nothrow) char[((int *) copyMe->bits)[0]];
if (bits == NULL)
{
cout << "ERROR Not enough memory.\n";
exit(1);
}
memcpy (bits, copyMe->bits, ((int *) copyMe->bits)[0]);
答案 0 :(得分:178)
我将违背一般智慧,std::copy
会有轻微的,几乎难以察觉的性能损失。我刚做了一个测试,发现这是不真实的:我确实注意到了性能差异。但是,获胜者是std::copy
。
我编写了一个C ++ SHA-2实现。在我的测试中,我使用所有四个SHA-2版本(224,256,384,512)散列5个字符串,并且我循环300次。我使用Boost.timer测量时间。 300循环计数器足以完全稳定我的结果。我分别运行了5次测试,在memcpy
版本和std::copy
版本之间交替进行。我的代码利用尽可能大的块来抓取数据(许多其他实现使用char
/ char *
,而我使用T
/ T *
(其中{ {1}}是用户实现中具有正确溢出行为的最大类型),因此对我所能使用的最大类型的快速内存访问是我算法性能的核心。这些是我的结果:
完成SHA-2测试运行的时间(以秒为单位)
T
std :: copy over memcpy的平均速度增加:2.99%
我的编译器是Fedora 16 x86_64上的gcc 4.6.3。我的优化标记为std::copy memcpy % increase
6.11 6.29 2.86%
6.09 6.28 3.03%
6.10 6.29 3.02%
6.08 6.27 3.03%
6.08 6.27 3.03%
。
Code for my SHA-2 implementations.
我决定对我的MD5实施进行测试。结果不太稳定,所以我决定进行10次运行。然而,在我的第一次尝试之后,我得到的结果在一次运行到另一次运行之间变化很大,所以我猜测正在进行某种操作系统活动。我决定重新开始。
相同的编译器设置和标志。只有一个版本的MD5,它比SHA-2更快,所以我在一组类似的5个测试字符串上做了3000个循环。
这是我最后的10个结果:
完成MD5测试运行的时间(以秒为单位)
-Ofast -march=native -funsafe-loop-optimizations
std :: copy over memcpy的平均速度下降:0.11%
Code for my MD5 implementation
这些结果表明我的SHA-2测试中使用了std :: copy的一些优化std::copy memcpy % difference
5.52 5.56 +0.72%
5.56 5.55 -0.18%
5.57 5.53 -0.72%
5.57 5.52 -0.91%
5.56 5.57 +0.18%
5.56 5.57 +0.18%
5.56 5.53 -0.54%
5.53 5.57 +0.72%
5.59 5.57 -0.36%
5.57 5.56 -0.18%
在我的MD5测试中无法使用。在SHA-2测试中,两个数组都是在调用std::copy
/ std::copy
的同一函数中创建的。在我的MD5测试中,其中一个数组作为函数参数传递给函数。
我做了一些测试,看看我能做些什么才能让memcpy
再次更快。答案结果很简单:打开链接时间优化。这些是我打开LTO的结果(选项-flto在gcc中):
使用-flto完成MD5测试运行的时间(以秒为单位)
std::copy
std :: copy over memcpy的平均增长率:0.72%
总之,使用std::copy memcpy % difference
5.54 5.57 +0.54%
5.50 5.53 +0.54%
5.54 5.58 +0.72%
5.50 5.57 +1.26%
5.54 5.58 +0.72%
5.54 5.57 +0.54%
5.54 5.56 +0.36%
5.54 5.58 +0.72%
5.51 5.58 +1.25%
5.54 5.57 +0.54%
似乎没有性能损失。事实上,似乎有性能提升。
结果说明
那么为什么std::copy
可以提升性能呢?
首先,我认为只要打开内联优化,任何实现都不会慢。所有编译器都积极地内联;它可能是最重要的优化,因为它可以实现许多其他优化。 std::copy
可以(并且我怀疑所有现实世界的实现都可以)检测到参数是可以轻易复制的,并且内存是按顺序布局的。这意味着在最糟糕的情况下,当std::copy
合法时,memcpy
应该不会更糟。 std::copy
遵循std::copy
的简单实现应符合编译器的标准“在优化速度或大小时始终内联”。
但是,memcpy
还会保留更多信息。当您调用std::copy
时,该函数会保持类型不变。 std::copy
在memcpy
上运行,它会丢弃几乎所有有用的信息。例如,如果我传入一个void *
数组,编译器或库实现者可能能够利用std::uint64_t
的64位对齐,但使用{可能更难做到{1}}。像这样的算法的许多实现通过首先处理范围开始处的未对准部分,然后是对齐部分,然后是末端处的未对准部分来工作。如果保证所有对齐,则代码变得更简单,更快速,并且处理器中的分支预测器更容易正确。
过早优化?
std::copy
处于一个有趣的位置。我希望它永远不会慢于memcpy
,有时候使用任何现代优化编译器都会更快。此外,您可以std::copy
进行任何操作,memcpy
。 memcpy
不允许缓冲区中的任何重叠,而std::copy
支持在一个方向上重叠(对于另一个重叠方向,memcpy
支持重叠)。 std::copy
仅适用于指针,std::copy_backward
适用于任何迭代器(memcpy
,std::copy
,std::map
或我自己的自定义类型)。换句话说,当您需要复制数据块时,您应该只使用std::vector
。
答案 1 :(得分:77)
我知道的所有编译器都会在适当的时候用std::copy
替换一个简单的memcpy
,或者更好的是,将副本矢量化,使其比memcpy
更快。
无论如何:简介并找出自己。不同的编译器会做不同的事情,很可能它不会完全按你的要求做。
见this presentation on compiler optimisations(pdf)。
对于POD类型的简单std::copy
,此处为what GCC does。
#include <algorithm>
struct foo
{
int x, y;
};
void bar(foo* a, foo* b, size_t n)
{
std::copy(a, a + n, b);
}
以下是反汇编(仅-O
优化),显示对memmove
的调用:
bar(foo*, foo*, unsigned long):
salq $3, %rdx
sarq $3, %rdx
testq %rdx, %rdx
je .L5
subq $8, %rsp
movq %rsi, %rax
salq $3, %rdx
movq %rdi, %rsi
movq %rax, %rdi
call memmove
addq $8, %rsp
.L5:
rep
ret
如果将功能签名更改为
void bar(foo* __restrict a, foo* __restrict b, size_t n)
然后memmove
成为memcpy
,以提高性能。请注意,memcpy
本身将被大量矢量化。
答案 2 :(得分:23)
始终使用std::copy
,因为memcpy
仅限于C风格的POD结构,如果目标位于std::copy
,编译器可能会将memcpy
的调用替换为std::copy
事实上POD。
另外,std::copy
可以与许多迭代器类型一起使用,而不仅仅是指针。 {{1}}更灵活,没有性能损失,是明显的赢家。
答案 3 :(得分:17)
理论上,memcpy
可能会有轻微的,难以察觉的,无穷小,性能优势,只是因为它没有与std::copy
具有相同的要求。来自memcpy
的手册页:
避免溢出,大小 目标指向的数组 和源参数,应为 至少num个字节,,不应该 重叠(用于重叠内存 块,memmove是一种更安全的方法。)
换句话说,memcpy
可以忽略数据重叠的可能性。 (将重叠数组传递给memcpy
是未定义的行为。)因此memcpy
不需要显式检查此条件,而只要std::copy
可以使用OutputIterator
参数不在源范围内。请注意,不与说明源范围和目标范围不能重叠相同。
因为std::copy
有一些不同的要求,理论上它应该略(特别强调略)慢,因为它可能会检查对于重叠的C数组,或者将C数组的复制委托给需要执行检查的memmove
。但在实践中,你(和大多数剖析器)可能甚至都不会发现任何差异。
当然,如果您不使用PODs,则无论如何都 。
答案 4 :(得分:9)
我的规则很简单。如果你使用C ++更喜欢C ++库而不是C:)
答案 5 :(得分:3)
只是一个小小的补充:{ path: '', redirectTo: '/admin', pathMatch: 'full' }
和memcpy()
之间的速度差异可能会有很大差异,具体取决于是启用还是禁用优化。使用g ++ 6.2.0并且没有优化std::copy()
明显胜出:
memcpy()
当启用优化(Benchmark Time CPU Iterations
---------------------------------------------------
bm_memcpy 17 ns 17 ns 40867738
bm_stdcopy 62 ns 62 ns 11176219
bm_stdcopy_n 72 ns 72 ns 9481749
)时,一切看起来几乎相同:
-O3
数组越大,效果越不明显,但即使在Benchmark Time CPU Iterations
---------------------------------------------------
bm_memcpy 3 ns 3 ns 274527617
bm_stdcopy 3 ns 3 ns 272663990
bm_stdcopy_n 3 ns 3 ns 274732792
N=1000
时,如果未启用优化,速度也会快两倍。
源代码(需要Google Benchmark):
memcpy()
答案 6 :(得分:2)
如果你真的需要最大的复制性能(你可能没有),不使用它们。
可以使用 lot 来优化内存复制 - 如果你愿意为它使用多个线程/核心,那就更多了。例如,见:
What's missing/sub-optimal in this memcpy implementation?
问题和一些答案都提出了实施或实施链接。
答案 7 :(得分:-2)
分析显示该语句:std::copy()
始终与memcpy()
一样快,或者更快为假。
我的系统:
HP-Compaq-dx7500-Microtower 3.13.0-24-generic#47-Ubuntu SMP Fri 5月2日 23:30:00 UTC 2014 x86_64 x86_64 x86_64 GNU / Linux。
gcc(Ubuntu 4.8.2-19ubuntu1)4.8.2
代码(语言:c ++):
const uint32_t arr_size = (1080 * 720 * 3); //HD image in rgb24
const uint32_t iterations = 100000;
uint8_t arr1[arr_size];
uint8_t arr2[arr_size];
std::vector<uint8_t> v;
main(){
{
DPROFILE;
memcpy(arr1, arr2, sizeof(arr1));
printf("memcpy()\n");
}
v.reserve(sizeof(arr1));
{
DPROFILE;
std::copy(arr1, arr1 + sizeof(arr1), v.begin());
printf("std::copy()\n");
}
{
time_t t = time(NULL);
for(uint32_t i = 0; i < iterations; ++i)
memcpy(arr1, arr2, sizeof(arr1));
printf("memcpy() elapsed %d s\n", time(NULL) - t);
}
{
time_t t = time(NULL);
for(uint32_t i = 0; i < iterations; ++i)
std::copy(arr1, arr1 + sizeof(arr1), v.begin());
printf("std::copy() elapsed %d s\n", time(NULL) - t);
}
}
g ++ -O0 -o test_stdcopy test_stdcopy.cpp
memcpy()个人资料:main:21:now:1422969084:04859已过:2650 us std :: copy()profile:main:27:now:1422969084:04862逝去:2745 us
memcpy()过去44 s std :: copy()过了45 sg ++ -O3 -o test_stdcopy test_stdcopy.cpp
memcpy()个人资料:main:21:now:1422969601:04939已过:2385我们
std :: copy()profile:main:28:now:1422969601:04941逝去:2690 us
memcpy()过了27 s std :: copy()过了43 s
Red Alert指出代码使用memcpy从数组到数组,std :: copy从数组到vector。这可能是更快记忆的原因。
因为有
v.reserve(的sizeof(ARR1));
复制到矢量或数组没有区别。
代码被修复为两种情况都使用数组。 memcpy仍然更快:
{
time_t t = time(NULL);
for(uint32_t i = 0; i < iterations; ++i)
memcpy(arr1, arr2, sizeof(arr1));
printf("memcpy() elapsed %ld s\n", time(NULL) - t);
}
{
time_t t = time(NULL);
for(uint32_t i = 0; i < iterations; ++i)
std::copy(arr1, arr1 + sizeof(arr1), arr2);
printf("std::copy() elapsed %ld s\n", time(NULL) - t);
}
memcpy() elapsed 44 s
std::copy() elapsed 48 s