[英]How to use SSE Intrinsics to subtract two different parts of the same array?
我有一個循環,其中另一個循環從數組中進行一些計算。 我想使用SSE優化代碼,但是有很多部分使我感到困惑,其中最大的問題在問題標題中有所說明。
原始代碼:
for (int j = 0; j < N; j++) {
for (int i = 0; i < N; i++) {
float kx = a[j] - a[i];
float ky = b[j] - b[i];
float kz = c[j] - c[i];
float k2 = kx*kx + ky*ky + kz*kz + eps;
float k2inv = 1.0f / sqrt(k2);
float k6inv = k2inv * k2inv * k2inv;
float s = m[j] * k6inv;
ax[i] += s * kx;
ay[i] += s * ky;
az[i] += s * kz;
}
}
如何將此代碼轉換為SSE指令? 我想出的代碼如下,但是當我意識到我需要減去同一數組的兩個部分時,我完全陷入了困境:
我的嘗試:
float *x = malloc(sizeof(float) * N);
float *y = malloc(sizeof(float) * N);
float *z = malloc(sizeof(float) * N);
for (int j = 0; j < N; j++) {
for (int i = 0; i < N; i++) {
__m128 rxj = _mm_load_ps(x+j);
__m128 rxi = _mm_load_ps(x+i);
__m128 ry = _mm_load_ps(y+j);
__m128 ry = _mm_load_ps(y+i);
__m128 rz = _mm_load_ps(z+j);
__m128 rz = _mm_load_ps(z+i);
}
}
我認為您不需要任何新的數組來向量化。 應用后restrict
關鍵字,(和不斷變化sqrt
到sqrtf
),原始源自動向量化鏗鏘3.7與-ffast-math
(但不是的gcc 5.3)。 您可能應該只使用OpenMP編譯指示或通過i或j啟用自動矢量化的功能。
// auto-vectorizes with clang and icc, but not gcc :/
void ffunc(float *restrict ax, float *restrict ay, float *restrict az,
const float *a, const float *b, const float *c,
int N, float eps, const float *restrict m)
{
for (int j = 0; j < N; j++) {
for (int i = 0; i < N; i++) {
float kx = a[j] - a[i];
float ky = b[j] - b[i];
float kz = c[j] - c[i];
float k2 = kx*kx + ky*ky + kz*kz + eps;
#if 1 // better code when rsqrtps is used (with a refinement step)
float k2inv = 1.0f / sqrtf(k2);
float k6inv = k2inv * k2inv * k2inv;
float s = m[j] * k6inv;
#else // maybe better code when rcpps isn't used
float k2sqrt = sqrtf(k2);
float k6sqrt = k2sqrt * k2sqrt * k2sqrt;
float s = m[j] / k6sqrt;
#endif
ax[i] += s * kx;
ay[i] += s * ky;
az[i] += s * kz;
}
}
}
如果可以為編譯器提供一些對齊保證,您也可能會獲得更好的代碼(特別是對於gcc,它喜歡做intro / outro塊來達到對齊邊界,而不是使用未對齊的操作。)
看起來您可以使用SSE一次執行內部循環的四個迭代。 並行執行四個i
值或並行執行四個j
值很好,因為一次迭代的結果不是另一次迭代的輸入。
因此,您將擁有一個具有a[i+3] a[i+2] a[i+1] a[i]
的向量(從向量中的左(高元素)到右(低元素))。 您將擁有三個僅在外循環中發生變化的向量,而三個在每次通過內循環時都會發生變化。 您將向另一個向量的所有位置廣播a[j]
(在內循環之外)。
實際上,您可能希望交換循環,因此累加器( ax[i] += ...
)可以位於整個內部循環中的寄存器中。 然后,您每次必須通過內部循環加載m[j]
,但不必每次都加載/存儲ax[i]
, ay[i]
, az[i]
來平衡這一點。
您還應該考慮在逆sqrt中需要多少精度:有一個互惠的sqrt指令,將其與一個或兩個newton-raphson迭代一起使用可能會提高吞吐量。 另外,這樣編寫您的源代碼:
// better code *if* the compiler isn't going to use rsqrtps
// otherwise worse code
float k2sqrt = sqrtf(k2); // note the sqrtf to not request double-precision sqrt
float k6sqrt = k2sqrt * k2sqrt * k2sqrt;
float s = m[j] / k6sqrt;
進行相同數量的除法(一),但乘數少一。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.