共计 2593 个字符,预计需要花费 7 分钟才能阅读完成。
使用 Intrinsics 方法,实现 SIMD 处理
使用 Intrinsics,可以利用硬件的 SIMD 指令进行处理。MMX,SSE,SSE2(AMD 已经通过交叉授权取得该技术)看起来真有那么美好么?让我们拭目以待。在处理中同样使用了定点数技术。
float test_SIMD_Filter() {
// 模拟申请 X1024 32bpp 的图像内存
__m128i* buf = (__m128i *)_mm_malloc(1024 * 1024 * sizeof(int),16);
// 0x0000FFFF0000FFFF0000FFFF0000FFFF 用于进行计算过滤比值的 65535
__m128i _65535 = _mm_set1_epi32(0xFFFF);
// 背景色,假设为 0xFFF8F8F8
__m128i pixel_bg = _mm_set_epi32(0x000000FF, 0x000000F8, 0x000000F8, 0x000000F8);
// 用于滤掉颜色中的 ALPHA 分量
__m128i noalpha_mask = _mm_set_epi32(0x000000FF, 0x000000FF, 0x0000000FF,0x0000000FF);
// 小于这个颜色的值将被滤掉
__m128i filter_val = _mm_set_epi32(0x00000000, 0x00000008, 0x000000008, 0x00000008);
__m128i* ptr = buf; // 图像数据指针
// 先初始化数据
for (int h = 0; h < 1024; h++) { // 按行循环
for (int w = 0; w < 1024 / 4; w++) {// 一次中四个点 (4X32 = 128)
// 将 4 个像素设为比较有特色的数值
*(__m128i *)ptr = _mm_set_epi32(0xFF112233, 0xFF445566, 0xFF778899, 0xFFAABBCC);
ptr++; // 下四个点
} // for
} // for
ptr = buf; // 开始模拟处理
BEGIN_PERF() // 开始计数
for (int h = 0; h < 1024; h++) { // 还是按行循环
for (int w = 0; w < 1024 / 4; w++) { // 一次四个点
__m128i pixel = *ptr; // 4 个像素:0xFF112233 0xFF445566 0xFF778899 0xFFAABBCC
// 取出前两个像素,成为->00FF, 0011, 0022, 0033, 00FF, 0044, 0055, 0066
__m128i pixel_1234 = _mm_unpacklo_epi8(pixel, _mm_setzero_si128());
// 取出前后两个像素,成为->00FF, 0077, 0088, 0099, 00FF, 00AA, 00BB, 00CC
__m128i pixel_5678 = _mm_unpackhi_epi8(pixel, _mm_setzero_si128());
// 因为涉及到 32 位乘法,所以还需要将像素的颜色分量扩展成 32 位格式
//->00000000 00000011 00000022 00000033 第一个像素
__m128i pixel_12 = _mm_unpacklo_epi8(pixel_1234, _mm_setzero_si128());
//->00000000 00000044 00000055 00000066 第二个像素
__m128i pixel_34 = _mm_unpackhi_epi8(pixel_1234, _mm_setzero_si128());
//->00000000 00000077 00000088 00000099 第三个像素
__m128i pixel_56 = _mm_unpacklo_epi8(pixel_5678, _mm_setzero_si128());
//->00000000 000000AA 000000BB 000000CC 第四个像素
__m128i pixel_78 = _mm_unpackhi_epi8(pixel_5678, _mm_setzero_si128());
// 先减,然后比较是否为 0,类似于传统方法中的减法和&操作
__m128i cmp_res = _mm_cmplt_epi32(_mm_sub_epi32(noalpha_mask, pixel_12), filter_val);
__m128i delta, bg; // 过滤比值,背景色
// 先判断第一个像素
if (_mm_cvtsi128_si32(cmp_res) != 0 ) { // 为了比较,需要转换成整数
// 计算过滤比值
delta = _mm_slli_epi32(pixel_12, 8);
// 分别计算位的低,高位,然后再或(高位要左移位),得到过滤后的背景色
bg = _mm_or_si128(_mm_mullo_epi16(pixel_bg, delta),
_mm_slli_epi32(_mm_mulhi_epu16(pixel_bg, delta), 16));
delta = _mm_sub_epi32(_65535, delta); // 65535- 比例为原像素的比例
// 与计算背景过滤的方法相同
pixel_12 = _mm_or_si128(_mm_mullo_epi16(pixel_12, delta),
_mm_slli_epi32(_mm_mulhi_epu16(pixel_12, delta), 16));
pixel_12 = _mm_srli_epi32(pixel_12, 16); // 从定点数还原
} // if
// 第三个像素
....
// 第四个像素略
......
// 最后还要将数据拼装回去
// 第 1,2 个像素
pixel_12 = _mm_packs_epi32(pixel_12, pixel_34);
// 第 3,4 个像素
pixel_56 = _mm_packs_epi32(pixel_56, pixel_78);
// 写回
*ptr++ = _mm_packs_epi16(pixel_12, pixel_56);
} // for
} // for
END_PERF() // 停止计时
_mm_free(buf); // 释放内存
return GET_PERF(); // 返回结果} // func
使用 SIMD,一次过处理 4 个像素,貌似很快的说,但各种扩展操作抵消了性能增长,因此速度大幅落后于传统算法!
good lucky!
正文完
发表至:无分类
2019-10-10