簡體   English   中英

如何使用SSE本征減去同一數組的兩個不同部分?

[英]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關鍵字,(和不斷變化sqrtsqrtf ),原始源自動向量化鏗鏘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;    
    }
  }
}

請參閱我對OP后續問題的回答,以獲得一個手動矢量化版本,該版本明顯優於gcc或clang所做的事情。

如果可以為編譯器提供一些對齊保證,您也可能會獲得更好的代碼(特別是對於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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM