基于AVX2实现打包32位整数与32位浮点数相乘的技术问询
问题解决与优化方案
一、合并寄存器的方法
你找不到VEXTRACTI128的逆指令,对应的指令是VINSERTI128,但因为最终要转回uint32,需要先把uint64结果提取低32位再合并,完整收尾代码如下:
// 将uint64结果的低32位提取为uint32 VPMOVQDQ(Y1, X1) // Y1中的4个uint64 → X1中的4个uint32 VPMOVQDQ(Y2, X2) // Y2中的4个uint64 → X2中的4个uint32 // 合并两个XMM寄存器为一个YMM寄存器(逆VEXTRACTI128操作) VINSERTI128(U8(1), X2, X1, Y0) // Y0即为最终的8个uint32结果寄存器
VINSERTI128(U8(1), X2, X1, Y0)的作用是:把X2的128位数据插入到X1的高128位位置(索引1),结果存入Y0,刚好还原最初拆分的value寄存器结构。
二、更优实现方案
结合你的需求(AVX2支持、[0,1)区间mean、独立元素乘法),有两种优化方向:
方案1:优化浮点计算流程
原方案的精度是最优的(float64可精确表示所有uint32),可简化步骤避免遗漏元素:
// 拆分mean为低/高128位,分别转成float64 VEXTRACTI128(U8(0), mean, X0) VCVTPS2PD(X0, Y3) // Y3存前4个mean的float64值 VEXTRACTI128(U8(1), mean, X1) VCVTPS2PD(X1, Y4) // Y4存后4个mean的float64值 // 处理value的低4个uint32 VEXTRACTI128(U8(0), value, X2) VPMOVZXDQ(X2, Y1) VCVTUDQ2PD(Y1, Y1) VMULPD(Y3, Y1, Y1) VCVTPD2UDQ(Y1, Y1) // 处理value的高4个uint32 VEXTRACTI128(U8(1), value, X3) VPMOVZXDQ(X3, Y2) VCVTUDQ2PD(Y2, Y2) VMULPD(Y4, Y2, Y2) VCVTPD2UDQ(Y2, Y2) // 合并结果 VPMOVQDQ(Y1, X1) VPMOVQDQ(Y2, X2) VINSERTI128(U8(1), X2, X1, Y0)
方案2:定点数优化(性能优先)
因为mean ∈ [0,1),可将其转换为Q32定点数(即mean * 2^32后转成uint32),全程用整数指令计算,性能比浮点方案更高,精度损失在多数估计算法场景可接受:
// 1. 将mean(float32)转成Q32定点数(mean * 2^32) // 提前在数据段定义常量:8个4294967296.0f(2^32的float32值) VMOVAPS(const_2pow32, Y4) VMULPS(mean, Y4, Y4) // mean每个元素乘以2^32 VCVTPS2UDQ(Y4, Y4) // 转成uint32定点数,就近舍入 // 2. 处理低4个元素的乘法 VEXTRACTI128(U8(0), Y4, X0) VEXTRACTI128(U8(0), value, X1) VPMULUDQ(X0, X1, Y0) // uint32相乘得到uint64 VPSRLQ(U32(32), Y0, Y0) // 右移32位等价于除以2^32 VPMOVQDQ(Y0, X0) // 转成uint32 // 3. 处理高4个元素的乘法 VEXTRACTI128(U8(1), Y4, X2) VEXTRACTI128(U8(1), value, X3) VPMULUDQ(X2, X3, Y1) VPSRLQ(U32(32), Y1, Y1) VPMOVQDQ(Y1, X2) // 4. 合并结果 VINSERTI128(U8(1), X2, X0, Y0)
三、方案选择
- 若追求最高精度:选择优化后的浮点方案,float64的53位尾数可精确还原所有uint32的乘法结果。
- 若追求最高性能:选择定点数方案,整数指令的吞吐量和延迟优于浮点指令,精度损失在多数场景可接受。
内容的提问来源于stack exchange,提问作者kevmo314
相关产品推荐
相关产品推荐

