嗨,我正在研究高斯模糊。我使用下面的函数来计算在其上应用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;
}
答案 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}
。
有关指南的链接,请参阅x86 wiki页面(例如,英特尔的内在指南可帮助您找到所需指令的C内在函数。)
就像我说的,这可能不是最佳的。 pmaddwd
是一个非常好的多重添加,具有16位输入和32位输出,但是对数据进行混洗,因此可以加在一起的元素水平相邻可能比仅使用较慢的pmulld
更多开销( SSE4.1正常32位打包乘法)。这样就可以一次处理多个像素,并在系数阵列的时间广播一个字。 (AVX2 vpbroadcastw
,或两步改组。)