将数据重新排列到这一点:
float *pointA_length;
float *pointB_width;
float *pointC_height;
,可能需要你的数据结构的屠宰某种程度,所以你必须选择不管它是否值得。
现在我们能做的就是这样写:
void process_points(float* Alengths, float* Bwidths, float* Cheights,
float* output, int n)
{
for (int i = 0; i < n; i++) {
output[i] = sqrt(Alengths[i] * Alengths[i] +
Bwidths[i] * Bwidths[i] +
Cheights[i] * Cheights[i]);
}
}
写像这样使得它可以自动向量化。例如,针对AVX的GCC和-fno-math-errno -ftree-vectorize
可以矢量化该循环。尽管如此,它的确有很多的问题。 __restrict__
和对齐属性只会改善一点。所以这里有一个手矢量版本,以及:(未测试)
void process_points(float* Alengths,
float* Bwidths,
float* Cheights,
float* output, int n)
{
for (int i = 0; i < n; i += 8) {
__m256 a = _mm256_load_ps(Alengths + i);
__m256 b = _mm256_load_ps(Bwidths + i);
__m256 c = _mm256_load_ps(Cheights + i);
__m256 asq = _mm256_mul_ps(a, a);
__m256 sum = _mm256_fmadd_ps(c, c, _mm256_fmadd_ps(b, b, asq));
__m256 hsum = _mm256_mul_ps(sum, _mm256_set1_ps(0.5f));
__m256 invsqrt = _mm256_rsqrt_ps(sum);
__m256 s = _mm256_mul_ps(invsqrt, invsqrt);
invsqrt = _mm256_mul_ps(sum, _mm256_fnmadd_ps(hsum, s, _mm256_set1_ps(1.5f)));
_mm256_store_ps(output + i, _mm256_mul_ps(sum, invsqrt));
}
}
这使得一些假设:
- 所有的指针是32对齐。
n
是8的倍数,或者至少缓冲区有足够的填充,它们永远不会被超出界限访问。
- 输入缓冲区不与输出缓冲区混淆(它们可能是其中的别名,但是为什么)
- 以这种方式计算的平方根的精度稍微降低是可以的(精确到大约22位,而是正确舍入)。
- 与FMADD计算平方的总和可能会稍有不同比如果它使用乘法计算,并补充说,我认为这没什么太
- 目标支持AVX/FMA所以这将实际运行
的方法用于计算这里使用的平方根是使用近似倒数平方根,改进步骤(y = y * (1.5 - (0.5 * x * y * y))
),然后乘以x
,因为x * 1/sqrt(x) = x/sqrt(x) = sqrt(x)
。
是C还是C++? – Eregrith
这是C++我的不好。 – bakalolo
如何使用此功能?你真的需要sqrt,或者正方形适合你吗?或者如果它在一个循环中,你可能会得到向量化的循环。 – Petr