【发布时间】:2017-01-23 21:16:12
【问题描述】:
我编写了一小段代码,用于累积比 std::accumulate 更快的向量值,因为它允许对函数进行向量化。该函数的主要前提是该向量在累加后不再使用。代码如下:
template <typename floatType>
template <typename Iterator>
double Numeric_class<floatType>::AmDestructiveAccumulate(Iterator A, size_t length)
{
if (length == 1)
{
return A[0];
}
Iterator temp_;
while (length > 1)
{
if (length & 1) // odd
{
A[0] += A[length - 1]; // We add the last value which would otherwise be lost.
length >>= 1;
temp_ = A+length;
for (int i = 0; i < length; i++)
{
A[i] += temp_[i];
}
}
else // even
{
length >>= 1;
temp_ = A+length;
for (int i = 0; i < length; i++)
{
A[i] += temp_[i];
}
}
}
return A[0];
}
该函数基本上将向量分成两半,并取两半的成对和。在此之后,它将前半部分的总和分成两个等长的范围,然后再次成对地求和,依此类推。
我将此功能与std::vector<double> data 一起使用。如果我用 A 调用它是 data.data()。矢量化故事按预期进行,我的执行速度也显着提高。如果我使用 data.begin(),则不会发生矢量化。我使用 VC2015 编译了代码并进行了全面优化。是否有理由说明对代码的迭代器版本进行矢量化是非法的,或者 VC 只是不这样做,尽管它是合法的。
【问题讨论】:
-
TBH 这听起来像是一个可怕的算法,令人印象深刻的是优化器设法挽救了至少一些版本。您将线性访问 O(N) 算法变成了具有非线性访问的 O(N log N) 变体。
-
@MSalters 这是不正确的。访问仍然是 O(N)。它是 N+N/2+N/4+N/8 .... 这个几何级数收敛到 N*2。此外,访问在每次迭代中都是线性的。最后但同样重要的是:分析显示相关 N 的性能有所提高。
-
公平点,它看起来像两个长度为 N/2 的内循环,用于奇数和偶数,但只使用了两个中的一个,因此它不是
N+2*(N/2)+4*(N/4)+...。 -
gcc 为两个版本生成几乎相同的目标代码。 VS2015 产生了相当不同的目标文件,但并没有那么离谱,我在这两种情况下都看到了矢量化指令。你可以在某个粘贴箱中发布这两个版本的程序集列表吗?
标签: c++ vector iterator vectorization