4. 向量乘法运算
任务目标
实现两个长度相同的向量逐元素相乘(Hadamard积),将结果存储到第三个向量中。本案例使用RISC-V向量扩展指令完成核心计算,并通过C语言封装调用验证结果正确性。
环境与依赖
- 支持RISC-V向量扩展(V扩展)的编译器:
riscv64-unknown-elf-gcc(需支持-march=rv64gcv) - 模拟器或开发板:QEMU(
qemu-riscv64)或等效环境
核心实现
以下代码定义向量乘法函数,使用向量加载、乘法、存储指令完成逐元素相乘。向量长度通过vsetvli动态设置,循环处理可能超过向量寄存器长度的数据。
#include <stdint.h>
#include <stddef.h>
// 向量逐元素乘法: dst[i] = src1[i] * src2[i]
void vec_mul_int32(int32_t *dst, const int32_t *src1, const int32_t *src2, size_t n) {
size_t i = 0;
while (i < n) {
size_t vl = __builtin_riscv_vsetvli(n - i, __RISCV_VXRM_RNU, __RISCV_VSEW_32);
vint32m1_t v1 = __builtin_riscv_vle32_v_i32m1(src1 + i, vl);
vint32m1_t v2 = __builtin_riscv_vle32_v_i32m1(src2 + i, vl);
vint32m1_t vres = __builtin_riscv_vmul_vv_i32m1(v1, v2, vl);
__builtin_riscv_vse32_v_i32m1(dst + i, vres, vl);
i += vl;
}
}
验证入口
以下主函数构造固定输入,调用向量乘法并检查结果。预期输出为逐元素乘积,若全部匹配则打印PASS。
#include <stdio.h>
int main() {
const int32_t a[8] = {1, -2, 3, 4, -5, 6, 7, -8};
const int32_t b[8] = {2, 3, -1, 5, 2, -4, 1, 3};
int32_t c[8];
const int32_t expected[8] = {2, -6, -3, 20, -10, -24, 7, -24};
vec_mul_int32(c, a, b, 8);
int ok = 1;
for (int i = 0; i < 8; i++) {
if (c[i] != expected[i]) {
printf("Mismatch at [%d]: got %d, expected %d\n", i, c[i], expected[i]);
ok = 0;
}
}
if (ok) printf("PASS\n");
return ok ? 0 : 1;
}
编译与运行
riscv64-unknown-elf-gcc -march=rv64gcv -mabi=lp64d -O2 -o vec_mul vec_mul.c
qemu-riscv64 ./vec_mul
验证方法
- 程序输出
PASS且退出码为0,表示所有8个元素乘积与预期一致。 - 若输出
Mismatch at [i],则定位到对应索引,检查输入数据或向量指令使用是否正确。 - 可修改数组长度测试非向量寄存器倍数的边界情况(如
n=10),验证循环处理逻辑。
关键说明
__builtin_riscv_vsetvli动态设置向量长度,返回值是实际处理的元素个数,循环直至处理完所有元素。- 本实现使用
vint32m1_t(32位整数,单寄存器组),适用于任意长度向量;若需更高吞吐可改用m2/m4/m8,但需保证数据对齐。 - 乘法指令
vmul.vv执行两个向量寄存器逐元素相乘,结果类型与输入一致。