如何基于AVX2高效实现_mm256_mullo_epi4指令?
优化AVX2 4位逐组乘法(保留低4位)
无法直接减少_mm256_mullo_epi16的调用次数(因为每个4位limb的乘积是独立计算的,交叉相乘的项会干扰结果,必须单独处理),但可以通过优化指令布局、减少冗余操作来显著提升执行速度。
原代码的核心问题
原代码的思路是对每个16位元素中的四个4位limb分别移位到低4位,相乘后取低4位,再移位回原位置合并,但指令顺序没有充分利用CPU的乱序执行能力,且存在可优化的冗余操作。
优化后的实现
__attribute__((always_inline)) __m256i _mm256_mullo_epi4(const __m256i a, const __m256i b) { // 预定义4位掩码常量,避免每次调用重新生成 const __m256i mask_4bit = _mm256_set1_epi16(0x000F); // 提前计算所有移位后的操作数,让乘法指令可并行执行 const __m256i a_shr4 = _mm256_srli_epi16(a, 4); const __m256i b_shr4 = _mm256_srli_epi16(b, 4); const __m256i a_shr8 = _mm256_srli_epi16(a, 8); const __m256i b_shr8 = _mm256_srli_epi16(b, 8); const __m256i a_shr12 = _mm256_srli_epi16(a, 12); const __m256i b_shr12 = _mm256_srli_epi16(b, 12); // 并行执行乘法操作,利用CPU乱序执行隐藏乘法延迟 const __m256i mul0 = _mm256_and_si256(_mm256_mullo_epi16(a, b), mask_4bit); const __m256i mul1 = _mm256_and_si256(_mm256_mullo_epi16(a_shr4, b_shr4), mask_4bit); const __m256i mul2 = _mm256_and_si256(_mm256_mullo_epi16(a_shr8, b_shr8), mask_4bit); const __m256i mul3 = _mm256_and_si256(_mm256_mullo_epi16(a_shr12, b_shr12), mask_4bit); // 合并结果:各limb的结果位不重叠,用OR代替XOR(效果一致,可读性更强) return _mm256_or_si256(mul0, _mm256_or_si256(_mm256_slli_epi16(mul1, 4), _mm256_or_si256(_mm256_slli_epi16(mul2, 8), _mm256_slli_epi16(mul3, 12) ) ) ); }
关键优化点
- 预定义常量掩码:将
0x000F掩码定义为常量,编译器会将其放入常量池,避免每次调用重新生成。 - 提前计算移位操作数:将移位后的
a/b操作数提前计算,让后续的乘法指令可以被CPU乱序执行单元并行处理,隐藏乘法指令的延迟。 - 位操作等价替换:由于各limb的结果位区域完全不重叠(低4位、4-7位、8-11位、12-15位),XOR和OR的效果完全相同,OR指令的可读性更强。
- 强制内联:添加
always_inline属性,减少函数调用开销,让编译器可以进一步优化指令布局。
补充说明
针对AVX2平台,上述方案已是最优选择——因为四个4位limb的乘积必须独立计算,无法通过减少乘法指令次数来优化,只能通过指令调度和冗余操作消除来提升吞吐量。如果你的CPU支持AVX-512,还可以结合_mm512_shuffle_epi8和查找表(LUT)实现更高效的位并行乘法,但AVX2平台没有类似的直接优化空间。
内容的提问来源于stack exchange,提问作者PingFloyd
相关产品推荐
相关产品推荐

