当前位置:网站首页>NEON优化:矩阵转置的指令优化案例
NEON优化:矩阵转置的指令优化案例
2022-07-06 17:22:00 【来知晓】
NEON优化系列文章:
背景
矩阵运算中经常用到转置操作,这里将原子矩阵4x4的转置NEON优化案例总结一下。
原始C函数负责将M[4][4]
转置为MT[4][4]
,效果如下:
M[4][4]
- a1, b1, c1, d1
- a2, b2, c2, d2
- a3, b3, c3, d3
- a4, b4, c4, d4
M[4][4]^T
- a1, a2, a3, a4
- b1, b2, b3, b4
- c1, c2, c3, c4
- d1, d2, d3, d4
优化思路
有两种并行计算的方法将其转置。
- 方法1
- 用到4条指令:vld4q_f32/vtrnq_f32/vuzpq_f32/vst4q_f32
- ld4q先从内存一批交叉读入到寄存器中
- trnq利用内部两两转置功能,将部分行列进行转置
- uzpq利用解交织的读写功能,将部分行列进行转置
- 然后利用寄存器单独赋值给不同行
- st4q将寄存器结果写入到内存中
- 方法2
- 利用内存和寄存器的交叉读写关系,两条指令实现转置,不用在寄存器中倒来倒去
- ld1q实现按行读取数据到寄存器
- st4q实现交叉读取寄存器值然后写入到内存中
样例代码
#include <stdio.h>
#include <stdint.h>
#include <arm_neon.h>
#define ROW_NUM 4
#define COL_NUM 4
int main(void)
{
// initial
float M[ROW_NUM][COL_NUM] = {
{
0, 1, 2, 3},
{
4, 5, 6, 7},
{
8, 9, 10, 11},
{
12, 13, 14, 15},
};
float MT[ROW_NUM][COL_NUM] = {
0};
// to do this:
// a1 b1 c1 d1 => a1 a2 a3 a4
// a2 b2 c2 d2 => b1 b2 b3 b4
// ...
// a4 b4 c4 d4 => d1 d2 d3 d4
// origin
int32_t i, j;
for (i = 0; i < ROW_NUM; i++) {
for (j = 0; j < COL_NUM; j++) {
MT[j][i] = M[i][j];
}
}
printf("ver1:\n");
for (i = 0; i < ROW_NUM; i++) {
for (j = 0; j < COL_NUM; j++) {
printf("%f ", MT[i][j]);
MT[i][j] = 0.;
}
printf("\n");
}
// method1
float32x4x4_t vf32x4x4fTmpABCD = vld4q_f32(&M[0][0]); // vf32x4x4fTmpABCD中val[0]: a1 b1 c1 d1, val[1]: a2 b2 c2 d2
float32x4x2_t vf32x4x2fTmpABCD01 = vtrnq_f32(vf32x4x4fTmpABCD.val[0], vf32x4x4fTmpABCD.val[1]); // vf32x4x2fTmpABCD01中val[0]: a1 a2 c1 c2, val[1]: b1 b2 d1 d2
float32x4x2_t vf32x4x2fTmpABCD23 = vtrnq_f32(vf32x4x4fTmpABCD.val[2], vf32x4x4fTmpABCD.val[3]);
float32x4x2_t vf32x4x2fTmpABCD02 = vuzpq_f32(vf32x4x2fTmpABCD01.val[0], vf32x4x2fTmpABCD23.val[0]); // row02, 按行组合
float32x4x2_t vf32x4x2fTmpABCD13 = vuzpq_f32(vf32x4x2fTmpABCD01.val[1], vf32x4x2fTmpABCD23.val[1]); // row13, 按行组合
vf32x4x2fTmpABCD02 = vtrnq_f32(vf32x4x2fTmpABCD02.val[0], vf32x4x2fTmpABCD02.val[1]);
vf32x4x2fTmpABCD13 = vtrnq_f32(vf32x4x2fTmpABCD13.val[0], vf32x4x2fTmpABCD13.val[1]);
vf32x4x4fTmpABCD.val[0] = vf32x4x2fTmpABCD02.val[0]; // a0 a1 a2 a3
vf32x4x4fTmpABCD.val[2] = vf32x4x2fTmpABCD02.val[1];
vf32x4x4fTmpABCD.val[1] = vf32x4x2fTmpABCD13.val[0];
vf32x4x4fTmpABCD.val[3] = vf32x4x2fTmpABCD13.val[1]; // d0 d1 d2 d3
vst4q_f32(&MT[0][0], vf32x4x4fTmpABCD);
printf("ver2:\n");
for (i = 0; i < ROW_NUM; i++) {
for (j = 0; j < COL_NUM; j++) {
printf("%f ", MT[i][j]);
MT[i][j] = 0.;
}
printf("\n");
}
// method2
float32x4x4_t vf32x4x4fTmp1ABCD;
vf32x4x4fTmp1ABCD.val[0] = vld1q_f32(&M[0][0]); // a1 b1 c1 d1
vf32x4x4fTmp1ABCD.val[1] = vld1q_f32(&M[1][0]);
vf32x4x4fTmp1ABCD.val[2] = vld1q_f32(&M[2][0]);
vf32x4x4fTmp1ABCD.val[3] = vld1q_f32(&M[3][0]); // a4 b4 c4 d4
vst4q_f32(&MT[0][0], vf32x4x4fTmp1ABCD); // 利用交叉读写特性,放入到MT数组
printf("ver3:\n");
for (i = 0; i < ROW_NUM; i++) {
for (j = 0; j < COL_NUM; j++) {
printf("%f ", MT[i][j]);
MT[i][j] = 0.;
}
printf("\n");
}
// 仅在寄存器内转置不输出到内存
// float fTmpABCD4x4[4][4]; // 临时中转数组
// vst4q_f32(&fTmpABCD4x4[0][0], vf32x4x4fTmpABCD); // 假设待转置的数据为vf32x4x4fTmpABCD
// vf32x4x4fTmpABCD.val[0] = vld1q_f32(&fTmpABCD4x4[0][0]);
// vf32x4x4fTmpABCD.val[1] = vld1q_f32(&fTmpABCD4x4[1][0]);
// vf32x4x4fTmpABCD.val[2] = vld1q_f32(&fTmpABCD4x4[2][0]);
// vf32x4x4fTmpABCD.val[3] = vld1q_f32(&fTmpABCD4x4[3][0]); // 转置结果放到寄存器中
return 0;
}
以上代码末尾,附有仅在寄存器中实现转置的demo,可根据具体场景使用。
小结
方法1
在寄存器内转置后输出到内存
只在寄存器内操作,6条指令,加4个赋值
方法2
- 直接通过内存与寄存器的交叉读取实现转置功能,命令降为3条。
- 寄存器和内存之间交叉读取实现,5条指令
总之,实践得知,只在寄存器内操作场景,法1更优,比内存和寄存器之间读写交互更高效,哪怕多了一两条指令。而需要将结果输出到内存的时候,法2更优。
边栏推荐
- [user defined type] structure, union, enumeration
- [software reverse - solve flag] memory acquisition, inverse transformation operation, linear transformation, constraint solving
- A brief history of deep learning (II)
- Data type of pytorch tensor
- Niuke cold training camp 6B (Freund has no green name level)
- [force buckle]41 Missing first positive number
- Cause of handler memory leak
- Equals() and hashcode()
- Installation and testing of pyflink
- ARM裸板调试之JTAG调试体验
猜你喜欢
C9 colleges and universities, doctoral students make a statement of nature!
Make a simple graphical interface with Tkinter
ESP Arduino (IV) PWM waveform control output
[hfctf2020]babyupload session parsing engine
Return to blowing marshland -- travel notes of zhailidong, founder of duanzhitang
Dell笔记本周期性闪屏故障
Trace tool for MySQL further implementation plan
Summary of being a microservice R & D Engineer in the past year
【案例分享】网络环路检测基本功能配置
[牛客] [NOIP2015]跳石头
随机推荐
动态规划思想《从入门到放弃》
【js】获取当前时间的前后n天或前后n个月(时分秒年月日都可)
Data type of pytorch tensor
迈动互联中标北京人寿保险,助推客户提升品牌价值
C9 colleges and universities, doctoral students make a statement of nature!
[HFCTF2020]BabyUpload session解析引擎
用tkinter做一个简单图形界面
"Exquisite store manager" youth entrepreneurship incubation camp - the first phase of Shunde market has been successfully completed!
深入探索编译插桩技术(四、ASM 探秘)
再聊聊我常用的15个数据源网站
OSPF configuration command of Huawei equipment
[software reverse automation] complete collection of reverse tools
Deep learning environment configuration jupyter notebook
筑梦数字时代,城链科技战略峰会西安站顺利落幕
A brief history of deep learning (II)
A brief history of deep learning (I)
Do you understand this patch of the interface control devaxpress WinForms skin editor?
深度学习简史(二)
随时随地查看远程试验数据与记录——IPEhub2与IPEmotion APP
String comparison in batch file - string comparison in batch file