最快50%缩放(A)RGB32图像使用sse intrinsic
Fastest 50% scaling of (A)RGB32 images using sse intrinsics
我想在c++中尽可能快地缩小图像。本文描述了如何有效地将32位rgb图像平均降低50%。它又快又好看。
我已经尝试使用sse intrinsic修改该方法。无论是否启用SSE,下面的代码都可以工作。然而,令人惊讶的是,加速几乎可以忽略不计。
有人能想到改进SSE代码的方法吗?创建变量shuffle1和shuffle2的两行似乎是两个候选(使用一些巧妙的移动或类似的方法)。
/*
* Calculates the average of two rgb32 pixels.
*/
inline static uint32_t avg(uint32_t a, uint32_t b)
{
return (((a^b) & 0xfefefefeUL) >> 1) + (a&b);
}
/*
* Calculates the average of four rgb32 pixels.
*/
inline static uint32_t avg(const uint32_t a[2], const uint32_t b[2])
{
return avg(avg(a[0], a[1]), avg(b[0], b[1]));
}
/*
* Calculates the average of two rows of rgb32 pixels.
*/
void average2Rows(const uint32_t* src_row1, const uint32_t* src_row2, uint32_t* dst_row, int w)
{
#if !defined(__SSE)
for (int x = w; x; --x, dst_row++, src_row1 += 2, src_row2 += 2)
* dst_row = avg(src_row1, src_row2);
#else
for (int x = w; x; x-=4, dst_row+=4, src_row1 += 8, src_row2 += 8)
{
__m128i left = _mm_avg_epu8(_mm_load_si128((__m128i const*)src_row1), _mm_load_si128((__m128i const*)src_row2));
__m128i right = _mm_avg_epu8(_mm_load_si128((__m128i const*)(src_row1+4)), _mm_load_si128((__m128i const*)(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_row, _mm_avg_epu8(shuffle1, shuffle2));
}
#endif
}
在通用寄存器和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]);
在相应的Shuffle操作的帮助下对SSE寄存器中的值进行Shuffle。
这应该是你要找的:
__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]
主要问题是使用_mm_set_epi32
来进行洗牌——不像大多数SSE内部函数,这并不直接映射到单个SSE指令——在这种情况下,它会在底层生成大量标量代码,并导致数据在内存、通用寄存器和SSE寄存器之间移动。请考虑使用适当的SSE shuffle特性。
第二个问题是,相对于加载和存储的数量,你所做的计算很少。这往往会导致代码带宽受限,而不是计算受限,即使使用理想的SSE代码,也可能看不到显著的性能改进。考虑在循环中组合更多操作,以便在缓存中对数据进行更多操作。
如果SSE本质没有什么区别,那么代码可能受到内存带宽的限制。
在你的代码中有很多的加载和存储,(_mm_set_epi32
是一个加载以及明显的)没有太多的实际工作。
如果加载/存储控制了运行时间,那么再多花哨的指令也救不了你。在高度流水线化和重新排序指令的现代处理器上,它可能在使整个处理器在非SSE版本的代码中保持忙碌方面做得很好。
您可以通过多种方式验证这种情况。最简单的方法可能是将算法的实际吞吐量与内存的加载/存储速度进行比较。您可能还会注意到一些差异,不仅改变了实现,而且改变了输入的大小,当输入超过每一级处理器缓存的大小时,输入的大小会急剧增加。
- C++,OpenCV,尝试显示图像时"OpenCV(4.3.0) Error: Assertion failed (size.width>0 && size.height>0)"此错误
- 如何使用OpenCV将RBG图像转换为HSV,并将H、S和V值保存为C++中的3个独立图像
- OpenCV EqualizeHist()从彩色图像创建黑白图像
- 将"打开的CV图像"中的"颜色"转换为整数格式
- 平均图像时图像损坏
- 在C++中使用GDAL可以将图像的像素坐标转换为lat,long吗
- 如何将图像传输到c++(dll)中的缓冲区,然后在c#的缓冲区中读/写
- Vulkan验证层不断在VkQueuePresentKHR()上抛出图像布局错误
- 使用FFMPEG将RGB图像序列保存到.mp4时出现问题
- 将RGB图像保存为PPM格式
- 将图像添加到资源文件夹UWP C++
- 彩色图像的卤化物处理平均值
- C++射线示踪剂ppm表示没有足够的数据来显示图像
- 重新定位图像时如何前进到下一个内存块
- 如何使用按钮更新GTK3图像以使用C++从相机捕获图片
- 为什么 BMP 图像上的 imwrite 会卡住/不返回?
- Gstreamer:每 5 秒使用多文件墨水保存图像/jpeg
- 使用 SSE 以最快的速度缩小 8 位灰度图像
- 带有SSE指令的图像最大值
- 最快50%缩放(A)RGB32图像使用sse intrinsic