基于SIMD优化12位整数数组位重排(CSI-2转DNG格式)
12位整数数组位重排的SIMD优化实现
位布局转换需求
原始MIPI CSI-2压缩格式的12位整数数组,每两个12位整数(A、B)占用3字节,位排列如下:
| byte0 | byte1 | byte2 | etc.. | A11 A10 A9 A8 A7 A6 A5 A4 | B11 B10 B9 B8 B7 B6 B5 B4 | B3 B2 B1 B0 A3 A2 A1 A0 | etc..
需要转换为Adobe DNG兼容的位排列格式:
| byte0 | byte1 | byte2 | etc.. | A11 A10 A9 A8 A7 A6 A5 A4 | A3 A2 A1 A0 B11 B10 B9 B8 | B7 B6 B5 B4 B3 B2 B1 B0 | etc..
现有逐字节实现
目前已实现逐3字节循环的处理代码,但性能无法满足需求:
void CSI2toBE12(uint8_t* pCSI2, uint8_t* pBE, uint8_t* pCSI2LineEnd) { while (pCSI2 < pCSI2LineEnd) { pBE[0] = pCSI2[0]; pBE[1] = ((pCSI2[2] & 0xf) << 4) | (pCSI2[1] >> 4); pBE[2] = ((pCSI2[1] & 0xf) << 4) | (pCSI2[2] >> 4); // 处理下一组12位像素对(3字节) pCSI2 += 3; pBE += 3; } }
SIMD优化方案(基于SIMDe兼容intrinsics)
目标CPU为64位ARM Cortex-A72(树莓派Compute Module 4),利用SIMDe可同时兼容ARM NEON和x86 AVX2指令集,以下是批量处理的实现:
1. ARM NEON(适配A72)+ SIMDe实现
一次处理8组(24字节)数据,大幅降低循环分支开销并提升吞吐量:
#include <simde/x86/avx2.h> #include <simde/arm/neon.h> void CSI2toBE12_SIMD(uint8_t* pCSI2, uint8_t* pBE, uint8_t* pCSI2LineEnd) { // 批量处理24字节对齐的数据块 while (pCSI2 + 24 <= pCSI2LineEnd) { // 加载24字节原始数据为3个8字节向量 simde_uint8x8_t vec0 = simde_vld1_u8(pCSI2); simde_uint8x8_t vec1 = simde_vld1_u8(pCSI2 + 8); simde_uint8x8_t vec2 = simde_vld1_u8(pCSI2 + 16); // 定义半字节提取掩码 const simde_uint8x8_t mask_low = simde_vdup_n_u8(0x0F); const simde_uint8x8_t mask_high = simde_vdup_n_u8(0xF0); // 拆分每个3字节组中的半字节 simde_uint8x8_t byte1_high = simde_vshr_n_u8(simde_vand_u8(vec1, mask_high), 4); simde_uint8x8_t byte1_low = simde_vand_u8(vec1, mask_low); simde_uint8x8_t byte2_low = simde_vand_u8(vec2, mask_low); simde_uint8x8_t byte2_high = simde_vshr_n_u8(simde_vand_u8(vec2, mask_high), 4); // 重组目标格式的字节 simde_uint8x8_t dst_vec1 = simde_vorr_u8(simde_vshl_n_u8(byte2_low, 4), byte1_high); simde_uint8x8_t dst_vec2 = simde_vorr_u8(simde_vshl_n_u8(byte1_low, 4), byte2_high); // 存储转换后的数据 simde_vst1_u8(pBE, vec0); simde_vst1_u8(pBE + 8, dst_vec1); simde_vst1_u8(pBE + 16, dst_vec2); // 推进指针到下一个数据块 pCSI2 += 24; pBE += 24; } // 处理剩余不足24字节的数据,复用原始逐字节逻辑 while (pCSI2 < pCSI2LineEnd) { pBE[0] = pCSI2[0]; pBE[1] = ((pCSI2[2] & 0xf) << 4) | (pCSI2[1] >> 4); pBE[2] = ((pCSI2[1] & 0xf) << 4) | (pCSI2[2] >> 4); pCSI2 += 3; pBE += 3; } }
2. AVX2实现(兼容x86平台)
若需在x86平台运行,利用AVX2指令集实现批量处理,通过SIMDe保证与NEON代码的兼容性:
#include <simde/x86/avx2.h> void CSI2toBE12_AVX2(uint8_t* pCSI2, uint8_t* pBE, uint8_t* pCSI2LineEnd) { while (pCSI2 + 24 <= pCSI2LineEnd) { // 加载24字节数据到3个128位向量 simde_mm128i vec0 = simde_mm_loadu_si128((simde__m128i*)pCSI2); simde_mm128i vec1 = simde_mm_loadu_si128((simde__m128i*)(pCSI2 + 8)); simde_mm128i vec2 = simde_mm_loadu_si128((simde__m128i*)(pCSI2 + 16)); // 定义半字节提取掩码 const simde_mm128i mask_low = simde_mm_set1_epi8(0x0F); const simde_mm128i mask_high = simde_mm_set1_epi8(0xF0); // 拆分半字节 simde_mm128i byte1_high = simde_mm_srli_epi16(simde_mm_and_si128(vec1, mask_high), 4); simde_mm128i byte1_low = simde_mm_and_si128(vec1, mask_low); simde_mm128i byte2_low = simde_mm_and_si128(vec2, mask_low); simde_mm128i byte2_high = simde_mm_srli_epi16(simde_mm_and_si128(vec2, mask_high), 4); // 重组目标字节 simde_mm128i dst_vec1 = simde_mm_or_si128(simde_mm_slli_epi16(byte2_low, 4), byte1_high); simde_mm128i dst_vec2 = simde_mm_or_si128(simde_mm_slli_epi16(byte1_low, 4), byte2_high); // 存储转换后的数据 simde_mm_storeu_si128((simde__m128i*)pBE, vec0); simde_mm_storeu_si128((simde__m128i*)(pBE + 8), dst_vec1); simde_mm_storeu_si128((simde__m128i*)(pBE + 16), dst_vec2); pCSI2 += 24; pBE += 24; } // 处理剩余数据 while (pCSI2 < pCSI2LineEnd) { pBE[0] = pCSI2[0]; pBE[1] = ((pCSI2[2] & 0xf) << 4) | (pCSI2[1] >> 4); pBE[2] = ((pCSI2[1] & 0xf) << 4) | (pCSI2[2] >> 4); pCSI2 += 3; pBE += 3; } }
优化说明
- 批量处理:一次处理8组(24字节)数据,大幅减少循环分支的性能开销
- SIMDe兼容:代码可直接在ARM NEON和x86 AVX2平台编译运行,无需额外修改
- 内存对齐优化:若输入输出内存按16/32字节对齐,可将
_loadu_/_storeu_替换为_load_/_store_进一步提升内存访问效率
内容的提问来源于stack exchange,提问作者Russell Newman
相关产品推荐
相关产品推荐

