use*_*670 154 c++ optimization performance
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]);
Run Code Online (Sandbox Code Playgroud)
Dav*_*one 191
我将违背这里的一般智慧,这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 *(其中T是用户实现中具有正确溢出行为的最大类型),因此快速访问内存我可以使用的最大类型是我的算法性能的核心.这些是我的结果:
完成SHA-2测试运行的时间(以秒为单位)
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%
Run Code Online (Sandbox Code Playgroud)
std :: copy over memcpy的平均速度增加:2.99%
我的编译器是Fedora 16 x86_64上的gcc 4.6.3.我的优化标志是-Ofast -march=native -funsafe-loop-optimizations.
我决定对我的MD5实现进行测试.结果不太稳定,所以我决定进行10次运行.然而,在我的第一次尝试之后,我得到的结果在一次运行到另一次运行之间变化很大,所以我猜测正在进行某种操作系统活动.我决定重新开始.
相同的编译器设置和标志.只有一个版本的MD5,它比SHA-2更快,所以我在一组类似的5个测试字符串上做了3000个循环.
这是我最后的10个结果:
完成MD5测试运行的时间(以秒为单位)
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%
Run Code Online (Sandbox Code Playgroud)
std :: copy over memcpy的平均速度下降:0.11%
这些结果表明在我的SHA-2测试中使用的std :: copy有一些优化,这些优化std::copy在我的MD5测试中无法使用.在SHA-2测试中,两个数组都是在调用std::copy/ 的相同函数中创建的memcpy.在我的MD5测试中,其中一个数组作为函数参数传递给函数.
我做了一些测试,看看我能做些什么才能让它std::copy再次变得更快.答案结果很简单:打开链接时间优化.这些是我打开LTO的结果(选项-flto在gcc中):
使用-flto完成MD5测试运行的时间(以秒为单位)
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%
Run Code Online (Sandbox Code Playgroud)
std :: copy over memcpy的平均速度增加:0.72%
总之,使用时似乎没有性能损失std::copy.实际上,似乎有性能提升.
结果说明
那么为什么可以std::copy提升性能呢?
首先,我认为只要打开内联优化,任何实现都不会慢.所有编译器都积极地内联; 它可能是最重要的优化,因为它可以实现许多其他优化.std::copy可以(并且我怀疑所有现实世界的实现都会)检测到参数是可以轻易复制的,并且内存按顺序排列.这意味着在最坏的情况下,何时memcpy合法,std::copy应该表现不差.在简单的实现的std::copy可推迟到memcpy应符合"优化速度或大小时,总是内联这个"编译器的标准.
但是,std::copy也保留了更多的信息.当您调用时std::copy,该函数会保持类型不变.memcpy进行操作void *,丢弃几乎所有有用的信息.例如,如果我传入一个数组std::uint64_t,编译器或库实现者可能能够利用64位对齐std::copy,但可能更难以这样做memcpy.像这样的算法的许多实现通过首先处理范围开始处的未对准部分,然后是对齐部分,然后是末端处的未对准部分来工作.如果保证全部对齐,则代码变得更简单,更快速,并且处理器中的分支预测器更容易正确.
过早优化?
std::copy处于一个有趣的位置.我希望它永远不会比memcpy任何现代优化编译器慢,有时甚至更快.此外,任何事情,你可以memcpy,可以std::copy.memcpy不允许缓冲区中的任何重叠,而std::copy支持在一个方向上重叠(std::copy_backward对于另一个重叠方向).memcpy只适用于指针,std::copy在任何迭代器的工作原理(std::map,std::vector,std::deque,或者我自己的自定义类型).换句话说,您应该只std::copy在需要复制数据块时使用.
Pet*_*der 77
我所知道的所有编译器都会在适当std::copy的memcpy时候替换一个简单的,或者更好的是,将副本矢量化,使其比a更快memcpy.
在任何情况下:简介并找出自己.不同的编译器会做不同的事情,很有可能它不会完全按照你的要求做.
请参阅有关编译器优化的演示文稿(pdf).
以下是GCC为简单std::copy的POD类型所做的事情.
#include <algorithm>
struct foo
{
int x, y;
};
void bar(foo* a, foo* b, size_t n)
{
std::copy(a, a + n, b);
}
Run Code Online (Sandbox Code Playgroud)
这是反汇编(仅有-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
Run Code Online (Sandbox Code Playgroud)
如果您将功能签名更改为
void bar(foo* __restrict a, foo* __restrict b, size_t n)
Run Code Online (Sandbox Code Playgroud)
然后memmove成为一个memcpy稍微改善的性能.请注意,memcpy它本身将被大量矢量化.
Pup*_*ppy 23
始终使用std::copy,因为memcpy仅限于C-POD风格结构,编译器可能会取代调用std::copy与memcpy目标是否实际上POD.
另外,std::copy可以与许多迭代器类型一起使用,而不仅仅是指针.std::copy更灵活,没有性能损失,是明显的赢家.
Cha*_*via 17
理论上,memcpy可能具有轻微的,不易察觉的,无穷小的性能优势,仅仅因为它没有相同的要求std::copy.从手册页memcpy:
为避免溢出,目标和源参数指向的数组大小应至少为num个字节,并且不应重叠(对于重叠的内存块,memmove是一种更安全的方法).
换句话说,memcpy可以忽略重叠数据的可能性.(将重叠数组传递给memcpy未定义的行为.)因此memcpy不需要显式检查此条件,而std::copy只要OutputIterator参数不在源范围内就可以使用.请注意,这是不一样的话说,来源范围和目标范围不能重叠.
因此,由于std::copy有一些不同的要求,理论上它应该稍微(略微强调略微)较慢,因为它可能会检查重叠的C数组,或者委托复制C数组memmove,这需要执行校验.但在实践中,你(和大多数剖析器)可能甚至都不会发现任何差异.
当然,如果您不使用POD,则memcpy无论如何都无法使用.
Umm*_*mma 11
我的规则很简单.如果你使用C++更喜欢C++库而不是C :)
| 归档时间: |
|
| 查看次数: |
112026 次 |
| 最近记录: |