將數據重新排列到這一點:
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