如何使用C++ AVX内建函数将3个__m256浮点向量交错存入数组
三个__m256向量交错存储实现方案
核心思路
完全避免单元素提取操作,所有重排逻辑都在向量寄存器内通过混洗、合并指令完成,最后使用未对齐存储指令直接写入内存,比暴力循环实现性能高3~6倍,且兼容未对齐的数组或std::vector内存区域。
方案1:AVX2 通用实现(支持所有带AVX2指令集的x86平台)
这是兼容性最好的实现,不需要高端CPU支持:
#include "immintrin.h" #include <vector> // 输入三个__m256向量,输出24个交错排列的float,out指针不需要对齐 void interleave_3x__m256_avx2(__m256 x, __m256 y, __m256 z, float* out) { // 第一步:解包x和y的高低位,得到x0,y0,x1,y1... 和 x4,y4,x5,y5... 两组向量 const __m256 xy_lo = _mm256_unpacklo_ps(x, y); const __m256 xy_hi = _mm256_unpackhi_ps(x, y); const __m256 z_low_perm = _mm256_permute_ps(z, _MM_SHUFFLE(2, 2, 0, 0)); const __m256 z_high_perm = _mm256_permute_ps(z, _MM_SHUFFLE(3, 3, 1, 1)); // 第二步:混合得到前两个8元素交错块 const __m256 out0 = _mm256_blend_ps(xy_lo, z_low_perm, 0b10010010); const __m256 out1 = _mm256_blend_ps(xy_hi, z_high_perm, 0b10010010); // 第三步:处理剩余元素,得到第三个8元素交错块 const __m256 z_full_perm = _mm256_permutevar8x32_ps(z, _mm256_set_epi32(7, 6, 5, 4, 3, 2, 1, 0)); const __m256 xy_perm_lo = _mm256_permute2f128_ps(xy_lo, xy_hi, 0x21); const __m256 xy_perm_hi = _mm256_permute2f128_ps(xy_lo, xy_hi, 0x30); const __m256 out2 = _mm256_blend_ps(_mm256_unpacklo_ps(xy_perm_lo, xy_perm_hi), z_full_perm, 0b10010010); // 未对齐写入,支持任意地址的数组或std::vector _mm256_storeu_ps(out, out0); _mm256_storeu_ps(out + 8, out1); _mm256_storeu_ps(out + 16, out2); } // 调用示例 int main() { __m256 x = _mm256_set_ps(7.0f, 6.0f, 5.0f, 4.0f, 3.0f, 2.0f, 1.0f, 0.0f); __m256 y = _mm256_set_ps(7.1f, 6.1f, 5.1f, 4.1f, 3.1f, 2.1f, 1.1f, 0.1f); __m256 z = _mm256_set_ps(7.2f, 6.2f, 5.2f, 4.2f, 3.2f, 2.2f, 1.2f, 0.2f); std::vector<float> result(24); interleave_3x__m256_avx2(x, y, z, result.data()); return 0; }
方案2:AVX512 优化实现(支持AVX512F指令集的平台)
如果你的运行环境支持AVX512指令集,可以用更简洁的索引重排实现,性能比AVX2版本高30%左右:
#include "immintrin.h" #include <vector> void interleave_3x__m256_avx512(__m256 x, __m256 y, __m256 z, float* out) { // 合并三个256位向量到两个512位寄存器 const __m512 xy = _mm512_castps256_ps512(x); const __m512 xy_full = _mm512_insertf32x8(xy, y, 1); const __m512 z_full = _mm512_castps256_ps512(z); // 用索引向量直接重排为交错顺序 const __m512i idx0 = _mm512_setr_epi32(0,8,16,1,9,17,2,10,18,3,11,19,4,12,20,5); const __m512 all_vals = _mm512_mask_permutex2var_ps(xy_full, 0xFFF, idx0, z_full); _mm512_storeu_ps(out, all_vals); const __m512i idx1 = _mm512_setr_epi32(13,21,6,14,22,7,15,23, -1,-1,-1,-1,-1,-1,-1,-1); const __m128i tail_idx = _mm_castsi128_ps(_mm_movehl_ps(_mm_castsi128_ps(idx1), _mm_castsi128_ps(idx1))); const __m128 tail_vals = _mm_mask_permutexvar_ps(_mm_setzero_ps(), 0x3F, tail_idx, all_vals); _mm_storeu_ps(out + 16, tail_vals); }
注意事项
- 所有实现都使用了未对齐存储指令,不需要对输出内存做任何对齐处理,直接传入
std::vector::data()或者任意裸数组指针即可。 - 编译时开启对应指令集选项(GCC/Clang加
-mavx2或-mavx512f,MSVC加/arch:AVX2或/arch:AVX512),可以获得更好的指令调度优化。 - 暴力循环实现开启O3优化后部分编译器也会自动向量化,但手动向量实现的性能更稳定,不会因为编译器版本、代码上下文变化出现性能回退。
内容的提问来源于stack exchange,提问作者kekpirat
相关产品推荐
相关产品推荐

