NEON(ARM架构)与RISCV的Vector拓展(RISC-V架构)是两种针对SIMD(单指令多数据)并行计算的指令集扩展,其设计目标和应用场景有相似之处,但在实现方式、灵活性及生态支持等方面存在显著差异。以下从多个维度对比两者的异同:
1 相同点
1.1 SIMD加速目标
两者均旨在通过单指令操作多个数据,提升多媒体处理、科学计算、AI/机器学习等数据密集型任务的性能。
- NEON应用场景:
- 在视频编解码、图像处理中广泛应用
- 能够同时处理多个像素或数据元素
- 加速视频压缩和解压缩过程
- 快速进行图像滤波、边缘检测、色彩空间转换等操作
- RVV应用场景:
- 优化OpenCV、TensorFlow等框架的执行效率
- 提供更高效的向量指令集
- 加速图像识别、目标检测等任务
- 提升模型训练和推理速度
1.2 支持数据类型
- 均支持整数(8/16/32/64位)和浮点数(单精度/双精度)操作
- 可通过向量寄存器并行处理多个元素:
- NEON:128位寄存器可同时操作16个8位整数
- RVV:向量寄存器宽度可动态配置,如128,256,512,1024
1.3 应用场景重叠
两者均适用于需要高吞吐量的场景,如:
- 矩阵运算
- 信号处理
- 加密计算
2 不同点
2.1 应用领域扩展性
| 指令集 | 应用领域 | 作用 |
|---|---|---|
| NEON | 传统的嵌入式及移动终端 | 提升多媒体处理效率,优化用户体验 |
| RVV | 人工智能和高性能计算领域 | 支持机器学习和深度学习,高效处理大数据 |
2.2 寄存器设计与灵活性
| 指令集 | 寄存器长度 | 特点 |
|---|---|---|
| NEON | 固定长度 | 包括 16 个 128 位的 Q 寄存器或者 32 个 64 位的 D 寄存器, 数据类型和并行度由指令明确指定,指令阅读门槛较低 |
| RVV | 可变长度 | 支持可变长度向量寄存器(VLEN), 元素宽度(SEW)和向量长度(LMUL)能通过vsetvli指令动态配置, 能适配不同硬件环境,为开发者提供更大灵活性和可扩展性 |
2.3 指令集设计哲学
| 指令集 | 设计思想 |
|---|---|
| NEON | 提供了丰富全面的指令集, 支持加法、乘法、比较、移位、绝对值、极值运算等多种操作。 通过扩展功能(如饱和操作、数据类型转换等)增强了指令的灵活性。 |
| RVV | RISC 架构的核心思想是使用较少且简单的指令集。 每条指令通常只完成一个基本操作,从而实现高效的指令执行和更高的处理速度。 与复杂指令集(CISC)相比,RISC 的指令集更精简,硬件实现更简单, 强调通过优化的流水线设计和高效的指令调度来提高性能。 |
2.4 生态与兼容性
| 指令集 | 描述 |
|---|---|
| NEON | 在 ARM 生态中是强制性扩展,所有 Cortex-A 系列处理器均支持。 旨在为多媒体和信号处理等应用提供高效的向量处理能力,可并行处理多个数据元素提高处理速度, 但不同版本工具链支持的 NEON 版本不同,可能带来兼容性问题。 |
| RVV | 进迭时空在开源与生态建设方面正在大步前行,积极参与各类开源项目,与众多开发者共享技术成果, 不断推动技术的创新与发展。 同时,进迭时空还致力于构建一个活跃且包容的开源社区,为开发者们提供交流与合作的平台, 促进社区成员之间的知识共享和经验交流。 |
2.5 开发门槛
| 指令集 | 描述 |
|---|---|
| NEON | 依赖静态数据对齐和特定内存操作指令(如vld1/vst1)提升访存效率,需手动优化数据结构。 在实际应用中,开发人员需要仔细分析数据的存储方式和访问模式。 这一过程不仅需要对硬件架构有深入的理解,还需要具备丰富的编程经验和优化技巧。开发门槛较高。 |
| RVV | 通过内存连续访问自动向量化,支持非对齐访问,对普通数组即可启用 SIMD 加速,降低开发门槛。 RVV 技术的出现极大地简化了 SIMD 编程的复杂性,它能够自动识别连续的内存访问模式,并将其向量化, 从而在无需手动调整数据结构的情况下,实现高效的并行计算。 这一特性不仅提高了开发效率,还减少了因手动优化不当而可能引入的错误。降低了开发门槛。 |
3 总结
RISC-V的主要优势
-
应用领域优势
- 在人工智能和高性能计算领域表现出色
- 为机器学习和深度学习提供有力支持
- 高效处理大量数据,加速模型训练和推理
- 为科学研究和工程应用提供强大计算能力
-
寄存器设计灵活
- 支持可变长度向量寄存器(VLEN)
- 元素宽度(SEW)和向量长度(LMUL)可通过vsetvli指令动态配置
- 适配不同硬件环境
- 为开发者提供更大灵活性和可扩展性
-
设计哲学先进
- 遵循RISC架构核心思想
- 使用精简指令集,每条指令完成一个基本操作
- 硬件实现简单
- 通过优化流水线设计和高效指令调度提高性能
-
开发门槛较低
- 支持内存连续访问自动向量化
- 支持非对齐访问
- 对普通数组即可启用SIMD加速
- 开发人员可专注于算法实现和业务逻辑
-
生态建设积极
- 开源社区建设持续推进
- 积极参与各类开源项目
- 共享技术成果
- 构建活跃包容的开源社区
4 参考代码
代码下载:add-examples
以下以向量加法为例,介绍NEON和RVV代码的异同:
4.1 NEON实现
详细代码可参考:
void vector_add_neon(int32_t *vec1, int32_t *vec2, int32_t *result, int size)
{
// 按照 4 个整数一组进行处理
for (int i = 0; i < size / 4; ++i)
{
// 加载向量数据到 NEON 寄存器
int32x4_t v1 = vld1q_s32(&vec1[i * 4]);
int32x4_t v2 = vld1q_s32(&vec2[i * 4]);
// 执行向量加法
int32x4_t vresult = vaddq_s32(v1, v2);
// 将结果存储回内存
vst1q_s32(&result[i * 4], vresult);
}
}
此代码仅供参考,旨在展示两种指令集在实际使用时的不同
使用NEON指令集实现向量加法的关键特点:
- 使用固定长度的向量寄存器
- 需要手动对齐数据
- 使用特定的向量加载/存储指令
4.2 RVV实现
代码可参考:
void vector_add_rvv(int32_t* vec1, int32_t* vec2, int32_t* result, size_t size) {
// 设置向量长度
size_t vl;
for (size_t i = 0; i < size; i += vl) {
// 动态设置向量长度,如果__riscv_vsetvlmax_e32m4()>size-i,长度会被设置为size-i,否则会被设置为__riscv_vsetvlmax_e32m4()
vl = __riscv_vsetvl_e32m4(size - i);
vint32m4_t v1 = __riscv_vle32_v_i32m4(&vec1[i], vl); // 加载 vec1
vint32m4_t v2 = __riscv_vle32_v_i32m4(&vec2[i], vl); // 加载 vec2
vint32m4_t vresult = __riscv_vadd_vv_i32m4(v1, v2, vl); // 向量加法
__riscv_vse32_v_i32m4(&result[i], vresult, vl); // 存储结果
}
}
使用RVV指令集实现向量加法的关键特点:
- 支持动态向量长度
- 自动处理数据对齐
- 简化的内存访问操作
编译运行:
# 交叉编译
riscv64-unknown-linux-gnu-gcc riscv_vector_add.c -march=rv64gcv -o ins.out -static
#本地编译
gcc riscv_vector_add.c -march=rv64gcv -o ins.out -static
#运行
./ins.out
运行结果:
9 9 9 9 9 9 9 9