矢量化 - 2022.1 简体中文

AI 引擎内核编码 最佳实践指南 (UG1079)

Document ID
UG1079
Release Date
2022-05-25
Version
2022.1 简体中文

以下显示了该矩阵乘法的标量参考代码示例。请注意,数据存储在列中。

void matmul_mat8_scalar(input_window_int16* matA,
		input_window_int16* matB,
		output_window_int16* matC){

	for(int i=0; i<M; i++){//M=64
		for(int j=0;j<L;j++){//L=2
			int temp = 0 ;
			for(int k=0; k<N; k++){//N=8
				temp += window_read(matA)*window_readincr(matB);//B is circular buffer, size N*L
				window_incr(matA,64); //Jump of 64 elements to access the next element of the same row
			}
			window_write(matC,(int16_t)(temp>>15)) ;
			window_incr(matC,64); //Jump to the next column
		}
		window_incr(matA,1); //Jump of one element for moving to the next row.
		window_incr(matC,1); //Jump to the next row
	}
}

正如前述示例中所分析的,mac16 内部函数是组合计算 16 条通道的最佳选择,因为可以同时加载来自一个列的 16 个 int16。要计算任一列中的 16 个输出数据,需要 4 次 mac16 运算。矢量“a”中的相同数据将使用两次,以便为两个输出列计算数据。因此,可以加载 2 列数据,并且有 2 项 mac16 用于累加至 2 个输出列。这 2 次加载和 2 项 MAC 会重复 4 次,以获取 2 个输出列的结果。以下伪代码中显示了此方法。

C_[0:15,0] = A_[0:15,0:1]*B_[0:1,0] 
C_[0:15,1] = A_[0:15,0:1]*B_[0:1,1] 

C_[0:15,0]+= A_[0:15,2:3]*B_[2:3,0] 
C_[0:15,1]+= A_[0:15,2:3]*B_[2:3,1]
 
C_[0:15,0]+= A_[0:15,4:5]*B_[4:5,0] 
C_[0:15,1]+= A_[0:15,4:5]*B_[4:5,1]

C_[0:15,0]+= A_[0:15,6:7]*B_[6:7,0] 
C_[0:15,1]+= A_[0:15,6:7]*B_[6:7,1]

在先前代码中,每个“*”都表示一项 MAC 运算。C_[0:15,0]C_[0:15,1] 表示单独累加的 2 个输出列。A_[0:15,0:1] 表示列 0 和 1,每个列都有 16 个元素。B_[0:1,0] 表示含 2 个元素的列 0。在真正矢量化的代码中,将会有一个代码循环,因为有 64 个输出行。要使用的 mac16 内部函数具有如下接口。

v16acc48 mac16	(	v16acc48 	acc,
	v64int16 	xbuff,
	int 	xstart,
	unsigned int 	xoffsets,
	unsigned int 	xoffsets_hi,
	unsigned int 	xsquare,
	v16int16 	zbuff,
	int 	zstart,
	unsigned int 	zoffsets,
	unsigned int 	zoffsets_hi,
	int 	zstep 
)	

缓冲器包含参数(起始、偏移、平方和阶跃)用于计算到缓冲器内的索引(矢量寄存器)。如需了解有关利用这些参数进行通道寻址的方案的详细信息,请参阅 MAC 内部函数

请注意,mac16 内部函数原型不同于先前矩阵矢量乘法示例中介绍的原型。此处的 xbuffv64int16,允许 2 组数据以交织方式来存储和使用。

下一节中介绍了如何利用 MAC 内部函数进行编码。