ARM NEON指令 浮点向量乘加(关联矩阵乘|卷积)

卷积可以通过im2col转化为矩阵乘。

而矩阵乘法,结果矩阵中的每个元素是等长度的一行与一列向量乘再累加求结果。

因此核心的矩阵算法如下:

float test(float* a, float* b, int n)
{
	float c = 0;
	for(int i=0; i

这段代码,如果手工优化,无非是循环展开等等...不过现代编译器的优化了得,在使用-ffast-math-ftree-vectorize优化选项后,编译器也能做到循环展开和向量化(使用SIMD)指令,如下是上述代码用g++ -S 生成的汇编代码

_Z4testPfS_i:
	subs	w8, w2, #1  // w8 = n-1, if w8 < 0, return 0
	b.lt	.LBB0_3

	add	x8, x8, #1 // x8+1 
	cmp		x8, #7 
	fmov	s0, wzr
	b.hi	.LBB0_4 // 如果数量超过8个,走循环

	mov	 w8, wzr
	b	.LBB0_8 // 跳转到尾巴处, 处理尾巴
	
.LBB0_3:
	mov	 w0, wzr
	ret
	
.LBB0_4:
	and	w9, w2, #0x7    // w9 = w2 & 0x7, 循环展开
	sub	x8, x8, x9 // 计算i的下标
	cbz	x8, .LBB0_8
	
	lsl	x13, x8, #2 // 根据i的下标计算偏移量,x13存放的是float的地址偏移量 左移2位,因为float是4B
	add	x10, x1, #16 // x1是b的地址, 不明白+16是何用意,x10是本次首地址
	add	x11, x0, #16 // x0是a的地址, 不明白+16是何用意,x11是本次首地址
	movi v0.2d, #0000000000000000 // 2个long等于4个float,赋值为0
	
	mov	 x12, x8
	
	add	 x0, x0, x13 // a的下一个地址
	add	 x1, x1, x13 // b的下一个地址
	movi v1.2d, #0000000000000000 // 2个long等于4个float,赋值为0
	
.LBB0_6:
	ldp	q2, q3, [x11, #-16] // 从x11-16的地址取8个float,放在q2和q3 中
	ldp	q4, q5, [x10, #-16] // 从x11-16的地址取8个float,放在q2和q3 中
	
	add	x10, x10, #32           // =32 // x10向上走8个float
	sub	x12, x12, #8            // =8 // 下标i减8
	add	x11, x11, #32           // =32 // x11向上走8个float
	fmla	v0.4s, v2.4s, v4.4s // 0 = 2 + 4 + 0,向量累加
	fmla	v1.4s, v3.4s, v5.4s // 1 = 3 + 5 + 1,向量累加
	
	cbnz	x12, .LBB0_6 // 判断循环是否到0

	fadd	v0.4s, v1.4s, v0.4s
	ext	v1.16b, v0.16b, v0.16b, #8
	
	fadd	v0.4s, v0.4s, v1.4s
	dup	v1.4s, v0.s[1]
	
	fadd	v0.4s, v0.4s, v1.4s
	cbz	w9, .LBB0_10
	
.LBB0_8:
	sub		w8, w2, w8
	
.LBB0_9:                  
	ldr	s1, [x0], #4 // 不足8个的,还剩一点尾巴,1个1个的取
	ldr	s2, [x1], #4
	sub	w8, w8, #1  // i下标1个1个处理
	fmadd	s0, s2, s1, s0  // 0 = 2 + 1 + 0, 单个float相加
	cbnz	w8, .LBB0_9
	
.LBB0_10:
	fcvtzs	w0, s0
	ret

 

你可能感兴趣的:(ARM)