在ARM架构中,SIMD(单指令多数据)和FP(浮点)指令集是现代处理器实现高性能计算的核心技术。这些指令通过并行处理多个数据元素,显著提升了多媒体处理、科学计算等场景的效率。作为ARMv8/v9架构的重要组成部分,SIMD&FP指令集为开发者提供了强大的向量运算能力。
NEON技术是ARM对SIMD指令的具体实现,它提供了:
LDUR(Load SIMD&FP Register with unscaled offset)指令用于从内存加载数据到SIMD&FP寄存器。其基本语法为:
assembly复制LDUR <Bt/Ht/St/Dt/Qt>, [<Xn|SP>{, #<simm>}]
指令编码格式如下:
code复制31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
size 1 1 1 1 0 0 x 1 0 imm9 0 0 Rn Rt
关键字段说明:
LDUR支持多种数据宽度加载:
assembly复制LDUR <Bt>, [<Xn|SP>{, #<simm>}] ; 8位加载
LDUR <Ht>, [<Xn|SP>{, #<simm>}] ; 16位加载
LDUR <St>, [<Xn|SP>{, #<simm>}] ; 32位加载
LDUR <Dt>, [<Xn|SP>{, #<simm>}] ; 64位加载
LDUR <Qt>, [<Xn|SP>{, #<simm>}] ; 128位加载
地址计算公式:
code复制address = X[n] + SignExtend(imm9, 64)
内存访问特性:
示例1:从内存加载32位浮点数
assembly复制LDUR S0, [X1, #4] ; 从X1+4地址加载32位数据到S0
示例2:从栈加载64位数据
assembly复制LDUR D1, [SP, #-8] ; 从SP-8地址加载64位数据到D1
MUL指令实现向量乘法运算,主要分为两种形式:
指令格式:
assembly复制MUL <Vd>.<T>, <Vn>.<T>, <Vm>.<T>
编码格式:
code复制31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
0 Q 0 0 1 1 1 0 size 1 Rm 1 0 0 1 1 1 Rn Rd U
支持的数据排列:
操作伪代码:
python复制for e in 0..elements-1:
element1 = Vn[e]
element2 = Vm[e]
Vd[e] = element1 * element2
指令格式:
assembly复制MUL <Vd>.<T>, <Vn>.<T>, <Vm>.<Ts>[<index>]
编码格式:
code复制31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
0 Q 0 0 1 1 1 1 size L M Rm 1 0 0 0 H 0 Rn Rd
元素索引计算:
示例1:16位向量乘法
assembly复制MUL V0.4H, V1.4H, V2.4H ; V0 = V1 * V2(逐元素)
示例2:32位向量与标量乘法
assembly复制MUL V0.2S, V1.2S, V2.S[1] ; V0[0] = V1[0]*V2[1], V0[1] = V1[1]*V2[1]
虽然LDUR支持非对齐访问,但为了提高性能:
乘法指令延迟:
最佳实践:
assembly复制; 不好的写法:存在写后读依赖
MUL V0.4S, V1.4S, V2.4S
ADD V3.4S, V0.4S, V4.4S
; 优化写法:插入独立指令打破依赖
MUL V0.4S, V1.4S, V2.4S
ADD V5.4S, V6.4S, V7.4S ; 独立指令
ADD V3.4S, V0.4S, V4.4S
非法指令异常:
性能未达预期:
结果不正确:
利用MUL和LDUR实现3x3卷积核计算:
assembly复制// 假设:
// X0: 输入图像指针
// X1: 输出图像指针
// V0-V2: 卷积核系数
// W2: 图像宽度
convolution_loop:
LDUR Q3, [X0, #0] // 加载第1行
LDUR Q4, [X0, #W2] // 加载第2行
LDUR Q5, [X0, #W2*2] // 加载第3行
MUL V6.4S, V3.4S, V0.4S // 第1行加权
MLA V6.4S, V4.4S, V1.4S // 累加第2行
MLA V6.4S, V5.4S, V2.4S // 累加第3行
STUR Q6, [X1], #16 // 存储结果
ADD X0, X0, #16 // 移动输入指针
CMP X0, X3 // 检查结束
B.LT convolution_loop
4x4矩阵乘法核心代码:
assembly复制// 假设:
// X0: 矩阵A指针
// X1: 矩阵B指针
// X2: 结果矩阵C指针
matmul_4x4:
// 加载矩阵B到寄存器V8-V11
LDUR Q8, [X1, #0]
LDUR Q9, [X1, #16]
LDUR Q10, [X1, #32]
LDUR Q11, [X1, #48]
// 计算第1行
LDUR Q0, [X0, #0] // 加载A的第1行
MUL V12.4S, V8.4S, V0.S[0]
MLA V12.4S, V9.4S, V0.S[1]
MLA V12.4S, V10.4S, V0.S[2]
MLA V12.4S, V11.4S, V0.S[3]
STUR Q12, [X2, #0] // 存储C的第1行
// 类似处理其他行...
利用不同位宽的MUL指令实现混合精度计算:
assembly复制// 16位输入,32位累加
LDUR H0, [X0, #0] // 加载16位数据
LDUR H1, [X1, #0]
SMULL V2.4S, V0.4H, V1.4H // 16->32位扩展乘法
结合乘加指令提高性能:
assembly复制// 使用MLA代替MUL+ADD
MUL V0.4S, V1.4S, V2.4S
ADD V3.4S, V0.4S, V3.4S // 2条指令
// 优化为
MLA V3.4S, V1.4S, V2.4S // 1条指令
在循环处理中合理搭配SIMD和标量指令:
assembly复制process_array:
CMP X0, X1
B.GE trailing_elements
// 主循环:处理4元素/迭代
LDUR Q0, [X0], #16
MUL V0.4S, V0.4S, V1.4S
STUR Q0, [X2], #16
B process_array
trailing_elements:
// 处理剩余1-3个元素
...