如何在启用AVX2的VCL中实现任意索引的Gather操作?
使用VCL实现动态索引的Gather向量化
问题背景
需要向量化处理一段随机索引的代码,对vSource中由vInd指定索引的元素执行操作,原始串行代码如下:
vector<int> vSource; vector<int> vInd; for (auto i = 0; i < vSource.size();i++) { vSource[ vInd[i] ]; // 执行具体操作 }
其中vInd包含完全随机的索引,无法通过洗牌等低成本方式优化。测试示例:
vector<int> vSource = {1,2,3,4,5,6,7,8,9,10,11,12,13}; vector<int> vInd = {2,1,5,3,10,5,8,2,10,2,5,3}; // 期望输出:3 2 6 4 11 6 9 3
现有实现问题
已通过AVX2 intrinsics实现向量化,但VCL的模板版本gather8i仅支持编译时常量索引,无法传入运行时动态索引:
AVX2 intrinsics 可行实现
void intrinGather(vector<int> & vSource, vector <int> & vInd) { __m256i ind = _mm256_loadu_si256((__m256i*) &vInd[0]); __m256i vec = _mm256_i32gather_epi32(&vSource[0], ind, 4); }
VCL 模板版本的局限
void VCLGather(vector<int> & vSource, vector<int> ind) { Vec8i vec; vec=gather8i<2,1,5,3,10,5,8,2>(&vSource[0]); // 编译通过 // vec=gather8i<ind[0],ind[3],ind[2],ind[10],ind[6],ind[8],ind[7],ind[1]>(&vSource[0]); // 编译失败 }
解决方案:VCL动态索引Gather接口
VCL提供了支持运行时动态索引的Gather接口,无需依赖编译时常量模板参数,同时保留VCL的多架构兼容特性。以AVX2对应的Vec8i为例,实现代码如下:
#include <VCL/Vec8i.h> #include <vector> void VCLDynamicGather(std::vector<int>& vSource, std::vector<int>& vInd) { // 按向量宽度(8个int)批量处理索引 size_t batchCount = vInd.size() / 8; for (size_t i = 0; i < batchCount; ++i) { // 加载8个动态索引到VCL向量 Vec8i indices = Vec8i::loadu(&vInd[i * 8]); // 执行Gather操作,步长为4(int类型的字节数) Vec8i result = Vec8i::gather(&vSource[0], indices, 4); // 对结果执行后续操作,例如存储或计算 // result.storeu(...); } // 处理剩余不足8个的元素 size_t remaining = vInd.size() % 8; for (size_t i = batchCount * 8; i < vInd.size(); ++i) { int val = vSource[vInd[i]]; // 剩余元素的串行处理逻辑 } }
关键说明
- 动态索引支持:VCL向量类型(如
Vec8i)的gather静态成员函数,接收同类型的向量索引参数,支持运行时加载的动态索引,逻辑与AVX2 intrinsics的_mm256_i32gather_epi32一致,但封装为VCL风格接口。 - 多架构兼容:该写法会自动适配VCL支持的指令集(SSE/AVX/AVX2等),编译时根据目标架构生成对应指令,无需手动区分。
- 内存对齐优化:若
vSource或vInd是对齐内存,可替换loadu/storeu为load/store提升性能;不确定对齐情况时,loadu/storeu更安全。 - 剩余元素处理:实际场景中需处理索引数量非向量宽度倍数的情况,剩余元素可通过串行循环或更小的向量类型(如
Vec4i)处理。
验证效果
使用用户提供的测试数据调用VCLDynamicGather,第一个向量块的result值为{3,2,6,4,11,6,9,3},与期望输出一致。
内容的提问来源于stack exchange,提问作者Vladislav
相关产品推荐
相关产品推荐

