1. 为什么我们需要矢量化?
在 Android 端侧推理(如 NCNN、MNN、TFLite)中,算子性能是核心。传统的标量计算(Scalar)一次只能处理一个数据,而 SIMD(Single Instruction Multiple Data)技术如 NEON 和 SVE,允许一条指令同时处理多个数据,这对于矩阵乘法、卷积等计算密集型算子至关重要。
2. NEON:经典的 128 位优化
NEON 是目前 Android 设备最普及的矢量指令集。它拥有 32 个 128 位的寄存器。在 float32 运算中,一个寄存器可以存放 4 个元素。
实战代码:向量加法 (NEON)
以下是使用 NEON Intrinsics 编写的向量加法示例:
#include <arm_neon.h>
void neon_vector_add(const float* a, const float* b, float* c, int n) {
int i = 0;
// 每次处理 4 个 float (128位 / 32位 = 4)
for (; i <= n - 4; i += 4) {
float32x4_t va = vld1q_f32(a + i);
float32x4_t vb = vld1q_f32(b + i);
float32x4_t vc = vaddq_f32(va, vb);
vst1q_f32(c + i, vc);
}
// 处理剩余的尾块(Scalar Fallback)
for (; i < n; i++) {
c[i] = a[i] + b[i];
}
}
3. SVE:迈向变长矢量的未来
随着 ARMv8.2-A 及更新架构(如骁龙 8 Gen 1、天玑 9000 等)的普及,SVE(Scalable Vector Extension)开始进入开发者视野。
SVE 最核心的变革是 VLA(Vector Length Agnostic),即代码不再硬编码矢量长度(128位或256位),而是通过硬件动态决定。
SVE 的两大优势:
- 变长寄存器:同一套二进制可以在不同位宽的 CPU 上跑出最优性能。
- 谓词寄存器 (Predication):通过掩码控制哪些元素参与运算,完美解决了 NEON 繁琐的“尾块处理”问题。
实战代码:SVE 版本的向量加法
#include <arm_sve.h>
void sve_vector_add(const float* a, const float* b, float* c, int n) {
// svcntw() 返回当前硬件支持的 32位 元素个数
// svwhilelt_b32 构建谓词掩码,自动处理边界,无需手动写尾块逻辑
for (int i = 0; i < n; i += svcntw()) {
svbool_t pg = svwhilelt_b32(i, n);
svfloat32_t va = svld1_f32(pg, a + i);
svfloat32_t vb = svld1_f32(pg, b + i);
svfloat32_t vc = svadd_f32_x(pg, va, vb);
svst1_f32(pg, c + i, vc);
}
}
4. 如何在 Android 项目中开启支持?
- 配置 NDK:建议使用 NDK r23 或更高版本。
- CMake 编译参数:
在 CMakeLists.txt 中针对支持 SVE 的源码文件设置:set_source_files_properties(sve_kernels.cpp PROPERTIES COMPILE_FLAGS "-march=armv8.2-a+sve") - 运行时检查:
由于并非所有手机都支持 SVE,必须在运行时检查 getauxval(AT_HWCAP) 是否包含 HWCAP_SVE,否则会触发非法指令崩溃。
5. 总结
从 NEON 到 SVE 的演进,不仅是位宽的提升,更是编程范式的简化。在高性能 AI 算子开发中,优先使用 NEON 保证兼容性,并针对高端芯片提供 SVE 优化路径,是目前端侧推理加速的主流方案。
汤不热吧