将高斯函数转换为 SSE
Posted
技术标签:
【中文标题】将高斯函数转换为 SSE【英文标题】:Converting gausian function into 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;
【问题讨论】:
英特尔在doing a Gaussian blur with AVX 上有一个教程/案例研究。他们对系数/数学使用浮点数。如果您只需要一个一维滤波器,请忽略水平通道转换其输出以设置垂直通道的部分。我还找到了a post on codereview.SE 我还找到了discussion in an issue on github for libass (a subtitle rendering library)。反正16位整数高斯系数就够精度了? 谢谢彼得,我会试试这个。我不确定高斯系数的精度,我对此有点陌生,所以我一直在尝试 【参考方案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
。
请参阅x86 wiki 页面以获取指南链接(如 Intel 的内在函数指南,可帮助您找到所需指令的 C 内在函数)。
就像我说的,这可能不是最优的。 pmaddwd
是一个非常不错的乘加法,具有 16 位输入和 32 位输出,但是对数据进行洗牌以使可以加在一起的元素水平相邻可能比仅使用较慢的 pmulld
开销更大(SSE4.1 正常32 位压缩乘法)。这样就可以一次处理多个像素,并在系数数组的时间广播一个单词。 (AVX2 vpbroadcastw
,或两步洗牌。)
【讨论】:
感谢彼得,我能够使用一些英特尔 AVX + 他们按照您所描述的方式做到这一点。非常感谢以上是关于将高斯函数转换为 SSE的主要内容,如果未能解决你的问题,请参考以下文章