在 ARM SVE(Scalable Vector Extension) intrinsics 指令集中,svld1_gather_s32index_f32 和 svst1_scatter_s32index_f32 是用于非连续内存访问的向量加载 / 存储指令,分别实现 “聚集加载(gather)” 和 “散列存储(scatter)” 操作。它们通过 32 位整数索引(index)定位内存地址,适用于处理稀疏数据或非连续数组元素的场景(如机器学习中的嵌入层、稀疏矩阵运算等)。
根据一组 32 位整数索引(相对于基地址的偏移量),从内存中非连续地加载单精度浮点数(float32) 到向量中,仅加载掩码(predicate)为1的元素位置。
svfloat32_t svld1_gather_s32index_f32(svbool_t pg, // 掩码:指示哪些元素需要加载const float32_t *base, // 基地址:索引的参考起点svint32_t indices // 32位整数索引向量:每个元素是相对于base的偏移量(单位:字节)
);
pg:svbool_t 类型的掩码,只有掩码位为1的位置会执行加载操作,其他位置结果未定义(通常用0填充)。
base:内存基地址,所有索引均相对于该地址计算实际访问地址(实际地址 = base + indices[i])。
indices:SVE 向量,每个元素是 32 位整数,表示相对于base的字节偏移量(需注意内存对齐,避免未定义行为)。
svfloat32_t 类型的向量,其中掩码pg为1的位置存储从base + indices[i]加载的float32值,其他位置无效(通常忽略)。
将向量中的单精度浮点数(float32)非连续地存储到内存,存储位置由一组 32 位整数索引(相对于基地址的偏移量)指定,仅存储掩码(predicate)为1的元素。
void svst1_scatter_s32index_f32(svbool_t pg, // 掩码:指示哪些元素需要存储float32_t *base, // 基地址:索引的参考起点svint32_t indices, // 32位整数索引向量:每个元素是相对于base的偏移量(单位:字节)svfloat32_t data // 待存储的单精度浮点数向量
);
pg:svbool_t 类型的掩码,只有掩码位为1的位置会执行存储操作。
base:内存基地址,实际存储地址为base + indices[i]。
indices:SVE 向量,每个元素是 32 位整数,表示相对于base的字节偏移量。
data:svfloat32_t 类型的向量,包含待存储的浮点数数据。
假设需要从一个稀疏数组中加载指定索引的元素并计算平均值,再将结果存储到另一个稀疏数组的指定位置。传统标量实现可能需要循环逐个访问,而 SVE 的 gather/scatter 指令可一次性处理多个元素:
#include <arm_sve.h>
#include <stdint.h>// 从稀疏数组加载元素并计算平均值,再存储到目标稀疏数组
void sparse_operation(const float* src, const int32_t* src_indices, float* dst, const int32_t* dst_indices, int count) {int i = 0;// 生成初始掩码:i < count 时置位(适配SVE向量长度)svbool_t pg = svwhilelt_b32(i, count);while (svptest_any(svptrue_b32(), pg)) {// 1. 加载当前批次的索引(src_indices[i], src_indices[i+1], ...)svint32_t src_idx = svld1_s32(pg, src_indices + i);// 2. 聚集加载:从src[src_indices[i]]加载float32元素svfloat32_t src_data = svld1_gather_s32index_f32(pg, src, src_idx);// 3. 计算平均值(示例:简单累加后除以向量长度)float sum = svaddv_f32(pg, src_data);int num_elements = svcntw(); // 获取32位元素的向量长度float avg = sum / num_elements;svfloat32_t avg_vec = svdup_f32(avg); // 广播平均值到向量// 4. 加载目标索引(dst_indices[i], ...)svint32_t dst_idx = svld1_s32(pg, dst_indices + i);// 5. 散列存储:将平均值存储到dst[dst_indices[i]]
svst1_scatter_s32index_f32(pg, dst, dst_idx, avg_vec);// 更新循环变量和掩码i += num_elements;pg = svwhilelt_b32(i, count);}
}
-
内存安全:
- 索引
indices必须指向有效内存地址(base + indices[i] 不能越界),否则会导致未定义行为(如崩溃)。
- 无需严格对齐(与普通
svld1不同),但对齐访问可提升性能。
-
掩码作用:
- 掩码
pg决定了哪些元素参与加载 / 存储,适用于处理长度非向量倍数的场景(如count不是svcntw()的整数倍)。
-
性能考量:
- Gather/Scatter 指令适用于非连续访问场景,但性能通常低于连续内存访问(如
svld1/svst1),应避免在连续数据上滥用。
- SVE 的向量长度可伸缩(如 128 位、256 位),指令会自动适配硬件,无需手动调整向量长度。
svld1_gather_s32index_f32:通过 32 位索引从非连续内存位置加载 float32 数据到向量,适合稀疏数据读取。
svst1_scatter_s32index_f32:通过 32 位索引将向量中的 float32 数据存储到非连续内存位置,适合稀疏数据写入。
两者是处理非连续内存访问的核心指令,在稀疏计算、机器学习等场景中可显著提升效率,充分发挥 SVE 的向量处理能力。