基于AVX2/AVX512的已排序寄存器合并与转置技术问询
基于AVX2/AVX512的并行排序后续优化问题解答
背景:我指导学生实现了基于AVX2和AVX512的并行排序代码,可同时对8或16个32位数值排序——加载8/16个寄存器,实现n=8或16的最优排序网络。以n=8为例,经19次交换完成排序后,8个AVX2寄存器中形成8组各含8个已排序数值的序列。针对以下技术问题给出具体实现方案:
问题1:无内存写入的寄存器转置方案
AVX2(8个ymm寄存器,每个8个32位值)
全程通过vpermd指令配合自定义掩码完成寄存器内转置,无需写入内存:
- 分组交叉:将8个寄存器分为4组(ymm0&ymm1、ymm2&ymm3、ymm4&ymm5、ymm6&ymm7),对每组用掩码
[0, 8, 1, 9, 2, 10, 3, 11]执行vpermd,生成中间寄存器,实现每组内的2x4转置。 - 二次交叉合并:将中间寄存器再分为两组,用新的掩码执行
vpermd,逐步完成8x8的全转置,最终每组原纵向排序的数值会存入单个ymm寄存器。
AVX512(16个zmm寄存器,每个16个32位值)
利用AVX512的宽掩码优势,用更少步骤完成:
- 先对寄存器做4x4分组转置,通过
vpermd提取交叉元素。 - 再通过两次全局交叉掩码的
vpermps操作,完成16x16的全转置,全程无内存交互。
问题2:合并两个已排序寄存器的最优代码序列
AVX2场景(合并ymm0/ymm1,生成有序的ymm2/ymm3)
本质是两个8元素有序数组的归并,拆分为两个8元素寄存器:
; 第一步:提取对应位置的最小/最大值,初步分离元素 vpminsd ymm2, ymm0, ymm1 ; ymm2 = 对应位置最小值集合 vpmaxsd ymm3, ymm0, ymm1 ; ymm3 = 对应位置最大值集合 ; 第二步:用掩码调整顺序,完成归并 vpermd ymm4, ymm2, [0, 2, 4, 6, 1, 3, 5, 7] ; 拆分ymm2为前后两半交叉 vpermd ymm5, ymm3, [0, 2, 4, 6, 1, 3, 5, 7] vpblendd ymm2, ymm4, ymm5, 0b10101010 ; 交叉合并得到前8个有序元素 vpermd ymm4, ymm2, [1, 3, 5, 7, 0, 2, 4, 6] ; 处理后半部分 vpermd ymm5, ymm3, [1, 3, 5, 7, 0, 2, 4, 6] vpblendd ymm3, ymm4, ymm5, 0b01010101 ; 得到后8个有序元素
AVX512场景(合并zmm0/zmm1,生成有序的zmm2/zmm3)
利用宽寄存器和掩码优势,简化步骤:
vpminsd zmm2, zmm0, zmm1 ; 提取对应位置最小值 vpmaxsd zmm3, zmm0, zmm1 ; 提取对应位置最大值 ; 用一次vpermd完成全归并排序,掩码需覆盖16个元素的归并顺序 vpermd zmm2, zmm2, [merge_mask_low] ; zmm2存前16个有序元素 vpermd zmm3, zmm3, [merge_mask_high] ; zmm3存后16个有序元素
其中merge_mask_low和merge_mask_high需根据归并排序的元素顺序预先定义,确保元素按全局升序排列。
问题3:纵向排序寄存器直接合并的实现方法
无需转置,可直接在8个纵向排序的寄存器上完成合并,核心是横向比较+定向移位:
- 全局排名定位:对每个寄存器的对应位置元素,用
vpcmpgtd与其他寄存器的同位置元素比较,生成掩码统计该元素的全局排名区间。 - 定向筛选移位:
- 用
vpminsd依次找出全局最小值,存入ymm2的对应位置; - 用
vpblendd将已选中的元素替换为极大值(比如0x7FFFFFFF),避免重复选中; - 重复上述过程,直到ymm2填满8个有序元素,剩余元素按同样逻辑存入ymm3。
以示例中的前两个元素为例:
- 用
- 先通过多轮
vpminsd找出全局最小的1(来自ymm0),放入ymm2[0]; - 再找出次小的4(来自ymm1),放入ymm2[1];
- 依次类推,直到ymm2填满前8个全局有序元素,剩下的元素存入ymm3。
这种方法省去了转置的开销,在合并规模较小时效率更优。
内容的提问来源于stack exchange,提问作者Dov
相关产品推荐
相关产品推荐

