【问题标题】:Converting gausian function into SSE将高斯函数转换为 SSE
【发布时间】:2016-02-15 18:58:53
【问题描述】:

您好,我正在研究高斯模糊。在应用一维高斯核后,我使用下面的函数来计算像素值。我想将此函数转换为非常高效的 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;
}

【问题讨论】:

标签: sse simd


【解决方案1】:

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

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


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

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

  • 加载 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 页面以获取指南链接(如 Intel 的内在函数指南,可帮助您找到所需指令的 C 内在函数)。

就像我说的,这可能不是最优的。 pmaddwd 是一个非常不错的乘加法,具有 16 位输入和 32 位输出,但是对数据进行洗牌以使可以加在一起的元素水平相邻可能比仅使用较慢的 pmulld 开销更大(SSE4.1 正常32 位压缩乘法)。这样就可以一次处理多个像素,并在系数数组的时间广播一个单词。 (AVX2 vpbroadcastw,或两步洗牌。)

【讨论】:

  • 感谢彼得,我能够使用一些英特尔 AVX + 他们按照您所描述的方式做到这一点。非常感谢
猜你喜欢
  • 2021-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-20
  • 2012-02-28
  • 2014-01-07
相关资源
最近更新 更多