将gausian函数转换为SSE

时间:2016-02-15 18:58:53

标签: sse simd

嗨,我正在研究高斯模糊。我使用下面的函数来计算在其上应用1 D高斯内核后的像素值。我想将此功能转换为非常高效的SSE,以便我可以获得显着的性能提升,但我从未使用它,所以无法写出正确的。有人可以帮我这个。

struct PixelValue   
{
    uint32_t R;  // 32bpp so that we don't overflow on below add
    uint32 G;  
    uint32 B;
    uint32 A;
};

Pixel FindPixelvalue(short* gausianFilter, short filterSize, unsigned int* pixels)
{
    Pixel out;
    const char* srcByte = reinterpret_cast< const char * >( pixels );

    while ( filterSize > 0 )
    {
       short value = *gausianFilter;

       out.R = out.R + *srcByte++ * value;
       out.G = out.G + *srcByte++ * value;
       out.B = out.B + *srcByte++ * value;
       out.A = out.A + *srcByte++ * value;

       gausianFilter++;
       filterSize--;
   }

   return out;
}

1 个答案:

答案 0 :(得分:1)

要获得最大的加速,您可能需要一次计算多个像素。尝试一次为单个像素获得SIMD加速将需要更多的随机播放。

我将假设您的像素颜色分量为uint8_t,即使您实际将其转换为char。 (char可以是签名或未签名的IDK,它位于Linux或Windows 64bit ABI中,因为如果有问题,你做错了。)

这是数据移动如何进行的第一次尝试。我认为这是次优的,有太多的改组。英特尔AVX case-study计算并行多行的结果,因此它们可以在乘法之前广播单个高斯系数,而不需要将多个系数混洗成模式。

  • 加载8个高斯系数(8个字的一个16B向量)

  • 加载8个连续像素(两个16B矢量,每个4像素):{R1 G1 B1 A1 R2 G2 B2 A2 ...}{R5 G5 B5 A5 ...}

  • 交错低半部分(punpcklbw),因此您拥有{R1 R5 G1 G5 B1 B5 A1 A5 R2 R6 ... }。 (后来,用高半部分再重复一遍)
  • 将零(punpcklbw / punpckhbw)解包为两个字元素向量

  • 将高斯系数混洗为{C1 C5 C1 C5 C1 C5 ...}

  • 系数和像素数据之间的
  • pmaddwd。它垂直倍增,然后将水平对添加到32位元素中。这是早期交错的动机,并安排高斯系数匹配。
  • 重复其他三组像素,{C2 C6 C2 C6 ...}系数
  • 将结果添加到累加器(paddd)。

最后,您将拥有一个包含四个元素的向量:{R G B A}

有关指南的链接,请参阅 wiki页面(例如,英特尔的内在指南可帮助您找到所需指令的C内在函数。)

就像我说的,这可能不是最佳的。 pmaddwd是一个非常好的多重添加,具有16位输入和32位输出,但是对数据进行混洗,因此可以加在一起的元素水平相邻可能比仅使用较慢的pmulld更多开销( SSE4.1正常32位打包乘法)。这样就可以一次处理多个像素,并在系数阵列的时间广播一个字。 (AVX2 vpbroadcastw,或两步改组。)