使用sse intrinsics对(A)RGB32图像进行最快的50%缩放 我想在C++中尽可能快地缩小图像。介绍如何有效地将32位rgb图像的平均值降低50%。它很快,看起来不错

使用sse intrinsics对(A)RGB32图像进行最快的50%缩放 我想在C++中尽可能快地缩小图像。介绍如何有效地将32位rgb图像的平均值降低50%。它很快,看起来不错,c++,sse,C++,Sse,我曾尝试使用sse内部函数修改该方法。无论启用或不启用SSE,下面的代码都有效。然而,令人惊讶的是,这种加速是可以忽略不计的 有谁能找到改进SSE准则的方法吗。创建变量shuffle1和shuffle2的两行代码似乎是两个候选代码(使用一些巧妙的移位或类似的方法) /* *计算两个rgb32像素的平均值。 */ 在线静态uint32平均值(uint32平均值、uint32平均值) { 返回((a^b)和0xfefefefel)>>1)+(a&b); } /* *计算四个rgb32像素的平均值。

我曾尝试使用sse内部函数修改该方法。无论启用或不启用SSE,下面的代码都有效。然而,令人惊讶的是,这种加速是可以忽略不计的

有谁能找到改进SSE准则的方法吗。创建变量shuffle1和shuffle2的两行代码似乎是两个候选代码(使用一些巧妙的移位或类似的方法)

/*
*计算两个rgb32像素的平均值。
*/
在线静态uint32平均值(uint32平均值、uint32平均值)
{
返回((a^b)和0xfefefefel)>>1)+(a&b);
}
/*
*计算四个rgb32像素的平均值。
*/
内联静态uint32平均值(常数uint32平均值[2],常数uint32平均值[2])
{
返回平均值(平均值(a[0],a[1]),平均值(b[0],b[1]);
}
/*
*计算两行rgb32像素的平均值。
*/
无效平均2行(常数uint32*src行1,常数uint32*src行2,uint32*dst行,int w)
{
#如果!已定义(_SSE)
对于(int x=w;x;--x,dst_行++,src_行1+=2,src_行2+=2)
*dst_行=平均值(src_行1,src_行2);
#否则
对于(int x=w;x;x-=4,dst_行+=4,src_行1+=8,src_行2+=8)
{
__m128i左=_mm_平均值_epu8(_mm_负载_si128((_m128i常数*)src_行1),_mm_负载_si128((_m128i常数*)src_行2));
__m128i right=_mm_avg_epu8(_mm_load_si128((_m128iconst*)(src_row1+4)),_mm_load_si128((_m128iconst*)(src_row2+4));
__m128i shuffle1=_mm_set_epi32(right.m128i_u32[2],right.m128i_u32[0],left.m128i_u32[2],left.m128i_u32[0]);
__m128i shuffle2=_mm_set_epi32(right.m128i_u32[3],right.m128i_u32[1],left.m128i_u32[3],left.m128i_u32[1]);
_mm_store_si128((__m128i*)dst_行,mm_avg_epu8(shuffle1,shuffle2));
}
#恩迪夫
}

如果SSE内部函数没有什么区别,那么代码可能会受到内存带宽的限制

代码中有很多加载和存储,实际工作不多(
\u mm\u set\u epi32
是一个加载,也是一个明显的加载)

如果加载/存储在运行时占主导地位,那么再多花哨的指令也救不了你。在高度流水线化和指令重新排序的现代处理器上,在代码的非SSE版本中保持整个处理器忙碌可能做得相当好


您可以通过多种方式验证这种情况。最简单的方法可能是测量算法的实际吞吐量与内存的加载/存储速度的比较。您可能还注意到一些差异,不仅通过改变实现,还通过改变输入的大小,随着输入超过每一级处理器缓存的大小而急剧增加。

主要问题是使用
\u mm\u set\u epi32
进行洗牌-与大多数SSE内部函数不同,这不会直接映射到单个SSE指令——在这种情况下,它会在后台生成大量标量代码,并导致数据在内存、通用寄存器和SSE寄存器之间移动。相反,请考虑使用适当的SSE shuffle intrinsic


第二个问题是,相对于加载和存储的数量,您所做的计算非常少。这将导致代码的带宽受限,而不是计算受限,即使使用理想的SSE代码,您也可能看不到显著的性能改进。请考虑在循环中组合更多操作,以便在数据处于缓存中时对其执行更多操作。

在通用寄存器和SSE寄存器之间传输数据的速度非常慢,因此应避免以下操作:

__m128i shuffle1 = _mm_set_epi32( right.m128i_u32[2], right.m128i_u32[0], left.m128i_u32[2], left.m128i_u32[0]);
__m128i shuffle2 = _mm_set_epi32( right.m128i_u32[3], right.m128i_u32[1], left.m128i_u32[3], left.m128i_u32[1]);
借助相应的洗牌操作,洗牌SSE寄存器中的值

这应该是您正在寻找的:

__m128i t0 = _mm_unpacklo_epi32( left, right ); // right.m128i_u32[1] left.m128i_u32[1] right.m128i_u32[0] left.m128i_u32[0]
__m128i t1 = _mm_unpackhi_epi32( left, right ); // right.m128i_u32[3] left.m128i_u32[3] right.m128i_u32[2] left.m128i_u32[2]
__m128i shuffle1 = _mm_unpacklo_epi32( t0, t1 );    // right.m128i_u32[2] right.m128i_u32[0] left.m128i_u32[2] left.m128i_u32[0]
__m128i shuffle2 = _mm_unpackhi_epi32( t0, t1 );    // right.m128i_u32[3] right.m128i_u32[1] left.m128i_u32[3] left.m128i_u32[1]

漂亮,你解决了。这两条线路把速度提高了125%。现在,算法只受到带宽的限制。