【问题标题】:Vectorizing a 2D stencil using SSE intrinsics使用 SSE 内在函数矢量化 2D 模板
【发布时间】:2012-06-22 17:36:20
【问题描述】:

我正在尝试仅使用对齐、加载和存储来矢量化 2D 模板。为此,我想基本上使用_mm_load_ps_mm_shuffle_ps 来获取所需的地址。

我的标量版本代码是:

    void FDTD_base (float *V, float *U, int dx, int dy, float c0, float c1, float c2, float c3, float c4)
    {
    int i, j, k;

            for (j = 4; j < dy-4; j++)
            {
                    for (i = 4; i < dx-4; i++)
                    {

                            U[j*dx+i] = (c0 * (V[j*dx+i]) //center
                                    + c1 * (V[j*dx+(i-1)] + V[(j-1)*dx+i] + V[j*dx+(i+1)] + V[(j+1)*dx+i] )
                                    + c2 * (V[j*dx+(i-2)] + V[(j-2)*dx+i] + V[j*dx+(i+2)] + V[(j+2)*dx+i] )
                                    + c3 * (V[j*dx+(i-3)] + V[(j-3)*dx+i] + V[j*dx+(i+3)] + V[(j+3)*dx+i] )
                                    + c4 * (V[j*dx+(i-4)] + V[(j-4)*dx+i] + V[j*dx+(i+4)] + V[(j+4)*dx+i] ));

                    }
            }

      }

到目前为止,我的矢量查看代码版本:

     for (j = 4; j < dy-4; j++)
    {
            for (i = 4; i < dx-4; i+=4)
            {
                    __m128 b = _mm_load_ps(&V[j*dx+i]);
                    center = _mm_mul_ps(b,c0_i);
                    a = _mm_load_ps(&V[j*dx+(i-4)]);
                    c = _mm_load_ps(&V[j*dx+(i+4)]);

                    d = _mm_load_ps(&V[(j-4)*dx+i]);
                    e = _mm_load_ps(&V[(j+4)*dx+i]);

                    u_i2 = _mm_shuffle_ps(a,b,_MM_SHUFFLE(1,0,3,2));//i-2
                    u_i6 = _mm_shuffle_ps(b,c,_MM_SHUFFLE(1,0,3,2));//i+2

                    u_i1 = _mm_shuffle_ps(u_i2,b,_MM_SHUFFLE(2,1,2,1));//i-1
                    u_i5 = _mm_shuffle_ps(b,u_i6,_MM_SHUFFLE(2,1,2,1));//i+1

                    u_i3 = _mm_shuffle_ps(a,u_i2,_MM_SHUFFLE(2,1,2,1));//i-3
                    u_i7 = _mm_shuffle_ps(u_i6,c,_MM_SHUFFLE(2,1,2,1));//i+3

                    u_i4 = a; //i-4
                    u_i8 = c; //i+4

谁能帮我获得 j-1,j+1 .....j-4,j+4 的位置。

这不起作用:

                    u_j2 = _mm_shuffle_ps(d,b,_MM_SHUFFLE(1,0,3,2));//j-2 (this is incorrect)
                    u_j6 = _mm_shuffle_ps(b,e,_MM_SHUFFLE(1,0,3,2));//j+2

                    u_j1 = _mm_shuffle_ps(u_j2,b,_MM_SHUFFLE(2,1,2,1));//j-1
                    u_j5 = _mm_shuffle_ps(b,u_j6,_MM_SHUFFLE(2,1,2,1));//j+1

                    u_j3 = _mm_shuffle_ps(d,u_j2,_MM_SHUFFLE(2,1,2,1));//j-3
                    u_j7 = _mm_shuffle_ps(u_j6,e,_MM_SHUFFLE(2,1,2,1));//j+3

                    u_j4 = d; //j-4 (this is fine)
                    u_j8 = e; //j+4

我只需要帮助来确定如何在不使用未对齐负载的情况下获取(j-1)*dx+i(j+1)*dx+1 .....(j-4)*dx+i(j+4)*dx+i

作为一个潜在的解决方案,我想将位移3*dx 添加到存储在d 中的地址以获得(j-1)*dx+i。并将3*dx的位移减去e中存储的地址,得到(j+1)*dx+i。 同理将2*dx添加到d的地址,得到j-2,以此类推。但我不知道使用 SSE 内在函数来实施此策略。

请帮忙。我正在使用英特尔 icc 编译器。

【问题讨论】:

  • @Paul:不完全是。我正在尝试确定在涉及步幅值时如何使用mm_shuffle_ps。上一篇文章是关于一维滑动窗口的。这是一个 2D 滑动窗口,它有一个新的维度,使它变得更加复杂。任何帮助都会非常有帮助
  • 您的所有约束都牢固吗?例如,绝对没有未对齐的加载和存储?还有其他表达相同计算的方法非常有效(尤其是在 Nehalem 和更高版本的 Intel CPU 上),但确实需要未对齐的加载和存储。

标签: c memory sse icc


【解决方案1】:

“谁能帮我获得 j-1,j+1 .....j-4,j+4 的位置。” - 这些不需要洗牌;它们已经与您的 SIMD 通道对齐。

u_j2 = _mm_load_ps(&V[(j-2)*dx+i]); 
u_j6 = _mm_load_ps(&V[(j+2)*dx+i]); 
u_j1 = _mm_load_ps(&V[(j-1)*dx+i]); 
u_j5 = _mm_load_ps(&V[(j+1)*dx+i]); 
// and so forth

绝对不能通过任何可能的重新排列从您标记为de 的变量中获得这些,因为d(例如)中的值是V[j-4, i], V[j-4, i+1], V[j-4, i+2], V[j-4, i+3],而您无法摆脱V[j-2, i]

提示:考虑 SIMD 通道;这清楚地表明您需要水平而不是垂直重新排列。

提示:考虑当内部循环计数器增加时会发生什么 (i+=4)。上一个循环中的 u_i5 (V[j, i+1..i+5]) 现在是当前循环中的 u_i3 (V[j, i-3..i+1])。您正在计算行中数据的每个偏移版本至少两次。您可能可以多次展开循环并避免做所有额外的工作。

提示:为什么不使用 AVX?使用 _mm256_permute_ps(如果需要,还可以使用 _mm256_permute2f128_ps)洗牌,以及相应的加载指令。它几乎快两倍,因为你有两倍宽的 SIMD 寄存器,并且大多数 AVX 指令在现代 CPU 上仍然只需要一个周期,与 SSE 指令相同。

【讨论】:

  • @Hello_PG:请看一下,如果这回答了您的问题,请告诉我。如果是这样,如果您接受并投票,我将不胜感激。谢谢!
猜你喜欢
  • 1970-01-01
  • 2011-10-18
  • 1970-01-01
  • 1970-01-01
  • 2020-02-11
  • 2012-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多