就性能而言,使用std :: memcpy()或std :: copy()会更好吗?

如下所示使用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]);

我会在这里反对一般智慧, 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%

std :: copy over memcpy速度总计平均增长率:2.99%

我的编译器是Fedora 16 x86_64上的gcc 4.6.3。 我的优化标志是-Ofast -march=native -funsafe-loop-optimizations

我的SHA-2实现的代码。

我决定也对我的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%

std :: copy over memcpy的总体平均速度下降:0.11%

我的MD5实现代码

这些结果表明,有一些优化是标准::在我的SHA-2测试使用复制std::copy不能在我的MD5测试使用。 在SHA-2测试中,两个数组都是在与std::copy / memcpy相同的函数中创建的。 在我的MD5测试中,其中一个数组作为函数参数传递给函数。

我做了一些更多的测试,看看我能做些什么来使std::copy再次更快。 答案很简单:打开链接时间优化。 这些是我打开LTO的结果(在gcc中选项-flto):

用-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%

std :: copy over memcpy的总速度平均增长速度:0.72%

总之,使用std::copy似乎并没有性能损失。 事实上,似乎有一个性能增益。

结果解释

那么为什么std::copy会提高性能呢?

首先,我不希望任何实现都会变慢,只要打开内联的优化。 所有编译器积极内联; 它可能是最重要的优化,因为它支持许多其他优化。 std::copy可以(并且我怀疑所有真实世界的实现都可以)检测到参数是可以复制的,并且内存按顺序排列。 这意味着在最坏的情况下,当memcpy是合法的, std::copy应该不会更糟。 遵循memcpystd::copy的简单实现应该符合编译器的标准:“在优化速度或大小时总是内联”。

但是, std::copy也保留了更多的信息。 当你调用std::copy ,函数保持类型不变。 memcpyvoid *上运行,它丢弃几乎所有有用的信息。 例如,如果我传入一个std::uint64_t数组,编译器或库实现者可能能够利用std::copy的64位对齐方式,但使用memcpy可能会更困难。 像这样的算法的许多实现首先在范围开始处的未对齐部分,然后是对齐部分,然后是未结束部分。 如果它们都保证一致,那么代码变得更简单和更快,并且更容易使处理器中的分支预测器正确。

过早优化?

std::copy处于一个有趣的位置。 我希望它永远不会比memcpy慢,有时使用任何现代优化编译器都会更快。 此外,任何你可以memcpy ,你可以std::copymemcpy不允许在缓冲区中有任何重叠,而std::copy支持一个方向上的重叠(对于另一个重叠方向, std::copy_backward )。 memcpy只适用于指针, std::copy适用于任何迭代器( std::mapstd::vectorstd::deque或我自己的自定义类型)。 换句话说,当你需要复制数据块时,你应该使用std::copy


我认识的所有编译器会在适当的时候用memcpy替换一个简单的std::copy ,甚至更好的是将拷贝向量化,以使它比memcpy更快。

无论如何:简介并找出你自己。 不同的编译器会做不同的事情,而且很可能它不会完全按照你的要求去做。

请参阅关于编译器优化的演示文稿(pdf)。

以下是GCC为简单的POD类型的std::copy所做的工作。

#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本身将严重矢量化。


总是使用std::copy因为memcpy仅限于C风格的POD结构,如果目标实际上是POD,编译器可能会用memcpy替换对std::copy调用。

另外, std::copy可以用于许多迭代器类型,而不仅仅是指针。 std::copy对于没有性能损失更加灵活,并且是明显的赢家。

链接地址: http://www.djcxy.com/p/36287.html

上一篇: Is it better to use std::memcpy() or std::copy() in terms to performance?

下一篇: Realistic usage of the C99 'restrict' keyword?