如何高效拆分单精度复数实部虚部到不同ymm寄存器?
优化复数序列拆分的AVX2实现
需求:将按[实部1, 虚部1, 实部2, 虚部2, …]格式存储的单精度复数序列加载到YMM寄存器,最终让ymm0存储所有实部、ymm1存储所有虚部。原实现使用4个跨通道洗牌操作,这类操作在部分设备上开销较高,希望找到更高效的实现方式。
原实现代码
// 此处负数代表虚部 float _f[] = {1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, 7, -7, 8, -8}; int i[] = {0, 2, 4, 6, 1, 3, 5, 7}; __m256 a = _mm256_loadu_ps(_f); __m256 b = _mm256_loadu_ps(_f+8); __m256i x = _mm256_loadu_si256((void*)i); __m256 c = _mm256_permutevar8x32_ps(a, x); __m256 d = _mm256_permutevar8x32_ps(b, x); __m256 e = _mm256_permute2f128_ps(c, d, 0x20); __m256 f = _mm256_permute2f128_ps(c, d, 0x31);
高效优化方案
可以利用AVX2的_mm256_shuffle_ps(128位内固定模式洗牌,延迟和吞吐量更优)替代原方案中开销较高的_mm256_permutevar8x32_ps,仅保留必要的跨128位块合并操作,将高开销跨通道操作从4个减少到2个。
优化后代码
float _f[] = {1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, 7, -7, 8, -8}; // 加载两组复数(每组4对)到YMM寄存器 __m256 a = _mm256_loadu_ps(_f); __m256 b = _mm256_loadu_ps(_f + 8); // 从每个寄存器内提取实部:每个128位块中选择索引0、2的元素 __m256 a_real = _mm256_shuffle_ps(a, a, 0x55); // 0x55对应二进制01010101 __m256 b_real = _mm256_shuffle_ps(b, b, 0x55); // 从每个寄存器内提取虚部:每个128位块中选择索引1、3的元素 __m256 a_imag = _mm256_shuffle_ps(a, a, 0xAA); // 0xAA对应二进制10101010 __m256 b_imag = _mm256_shuffle_ps(b, b, 0xAA); // 跨128位块合并实部和虚部,仅需两次跨通道操作 __m256 all_real = _mm256_permute2f128_ps(a_real, b_real, 0x20); __m256 all_imag = _mm256_permute2f128_ps(a_imag, b_imag, 0x20);
优化细节说明
- 替换高开销指令:原方案的
_mm256_permutevar8x32_ps是可变跨通道洗牌,在老AVX2设备(如Haswell之前型号)上延迟高、吞吐量低;优化后用_mm256_shuffle_ps(128位内固定洗牌)替代,该指令硬件支持更成熟,执行效率更高。 - 减少跨通道操作:原方案共4次跨通道操作,优化后仅保留2次
_mm256_permute2f128_ps用于合并两个寄存器的实部/虚部结果,进一步降低整体开销。 - 结果一致性:优化后的代码最终效果与原方案完全一致,
all_real存储所有实部、all_imag存储所有虚部。
内容的提问来源于stack exchange,提问作者Harris M Snyder
相关产品推荐
相关产品推荐

