否定std :: vector的最快方法

ena*_*one 26 c++ optimization blas stdvector lapack

假设我有一个double的std :: vector,即

std::vector<double> MyVec(N);
Run Code Online (Sandbox Code Playgroud)

在哪里N这么大,性能很重要.现在假设这MyVec是一个非平凡的向量(即它不是一个零向量,但已被某些例程修改).现在,我需要向量的否定版本:我需要-MyVec.

到目前为止,我一直在实施它

std::transform(MyVec.cbegin(),MyVec.cend(),MyVec.begin(),std::negate<double>());
Run Code Online (Sandbox Code Playgroud)

但是,实际上,我不知道这是否合情合理,或者只是我身边的超级天真.

我做得对吗?或者std :: transform在这种情况下只是一个超级慢的例程?

PS:我一直在使用BLAS和LAPACK库,但是我没有发现任何符合这种特殊需求的东西.但是,如果BLAS/LAPACK中存在比std :: transform更快的函数,我很高兴知道.

Col*_*Cat 28

#include <vector>
#include <algorithm>
#include <functional> 
void check()
{
    std::vector<double> MyVec(255);
    std::transform(MyVec.cbegin(),MyVec.cend(),MyVec.begin(),std::negate<double>());
}
Run Code Online (Sandbox Code Playgroud)

这个代码在https://godbolt.org/上,带有copile选项-O3,可以生成很好的程序集

.L3:
[...]
  cmp r8, 254
  je .L4
  movsd xmm0, QWORD PTR [rdi+2032]
  xorpd xmm0, XMMWORD PTR .LC0[rip]
  movsd QWORD PTR [rdi+2032], xmm0
.L4:
Run Code Online (Sandbox Code Playgroud)

很难想象更快.你的代码已经完美了,不要试图超越编译器并使用几乎每次都能运行的干净的C++代码.

  • 考虑到这个C++代码的高级特性,这个生成的代码实际上相当惊人. (22认同)
  • 如果你在编译器资源管理器上切换到clang-5.0.0,你将[见](https://godbolt.org/g/SAhvUj)它稍微展开循环.(务必将`cbegin`和`cend`更改为`begin`和`end`,否则它将无法编译). (6认同)
  • 分支上将有一个处理器停顿,你将根据`xorpd`的延迟松开周期.更优化的版本通常会在分支之前重复操作几次. (3认同)
  • @keith我怀疑现代x86 CPU是否属实.他们足够聪明,可以预测前面的几个分支,也可以重命名寄存器. (2认同)
  • @jpa处理器可能不会停止,但是如果处理器结束微操作或解码限制(除非它受内存限制,否则循环展开仍然是有益的). (2认同)

kei*_*ith 17

幸运的是,数据std::vector是连续的,所以你可以使用向量内在函数乘以-1(使用未对齐的加载/存储和特殊处理可能的溢出).或者使用ippsMulC_64f/ ippsMulC_64f_I来自intel的IPP库(你很难写出更快的东西),它将使用你平台上最大的矢量寄存器:https://software.intel.com/en-us/ipp-dev-reference-mulc

更新:为了澄清评论中的一些混淆,英特尔IPP的完整版本是免费的(虽然你可以支付支持),并且来自Linux,Windows和macOS.

  • @borisbn,现在有一个免费选项(适用于所有平台和全功能)付费选项和免费选项之间的唯一区别是免费选项除了提供帮助和提高的英特尔论坛外没有任何支持虫子 (2认同)