CNN/HighPerformanceComputing/Tengine/readme.md
基于ARM-v8的Tengine GEMM 矩阵乘法 汇编优化 教程
安装相关工具
sudo apt-get instal git cmake
git 是一个版本控制系统,稍后将用来从 github 网站上下载Tengine的源码
cmake 是一个编译工具,用来产生make过程中所需要的Makefile文件
安装支持库
sudo apt-get install libprotobuf-dev protobuf-compiler libboost-all-dev libgoogle-glog-dev libopencv-dev libopenblas-dev
protobuf 是一种轻便高效的数据存储格式,这是caffe各种配置文件所使用的数据格式
boost 是一个c++的扩展程序库,稍后Tengine的编译依赖于该库
google-glog 是一个google提供的日志系统的程序库
opencv 是一个开源的计算机视觉库
openblas 是一个开源的基础线性代数子程序库
特点
重点加速卷积等最为耗时的算子 convolution/FC/Pooling 支持多种卷积计算模式 GEMM/Direct/Winogrid
手工汇编调优,CPU微架构极致优化,Dataflow多线程加速,适配ARM A7/A17/A35/A53/A72/A73/A55/A76
支持F32/F16/Int8动态量化混合精度计算模式
老接口:
初始化 init_tengine();
载入模型,创建图 create_graph(nullptr, "tengine", tm_file) 普通设备
create_graph(nullptr, "tiny", tm_mem) // mcu stm32
create_graph(nullptr, "zhouyi", tm_file) // 周易 AIPU
create_graph(nullptr, "nnie", tm_file, config) // 海思 nnie 3519 3516
create_graph(nullptr, "rk3399pro", tm_mem) // rk3399pro AIPU
设置图属性 和 输入数据
get_graph_input_tensor(graph, 0, 0);
set_graph_attr(graph, "low_mem_mode", &val, sizeof(val));
预推理
prerun_graph(graph)
正式运行
run_graph(graph, 1)
清理
release_graph_tensor(input_tensor);
release_graph_tensor(output_tensor);
postrun_graph(graph);
destroy_graph(graph);
release_tengine();
新接口(类似ncnn的):
矩阵乘法的加速运算 A[M K] * B[K N] ====== C[M N]
纯c实现:
void gemm_pure_c(float* A, float* B, float* C,int m,int n,int k)
{
for(int i=0;i<M;i++) // 安装目标 C 矩阵维度遍历 先行维M
{
for(int j=0; j<N;j++) // 再 列维 N 主要是 矩阵 是 列优先排布
{
C[i*n+j]=0.f;
for(int p=0; p<K;p++) //A 矩阵每一行K个元素
{
C[i*N + j] += A[i*K + p] * B[p*N + j];
// C的i行j列 A的i行p列 B的p行j列
}
}
}
}
openblas 函数实现
数据并行SIMD NEON 向量优化
手写向量汇编优化
输入矩阵转换
----> 元素乘法 gemm算法 ----> 输出转换
权重矩阵转换
# kernel转换
G_F23 = np.array([
[ 1.0, 0.0, 0.0 ],
[ 0.5, 0.5, 0.5 ],
[ 0.5, -0.5, 0.5 ],
[ 0.0, 0.0, 1.0 ]])
# 输入转换矩阵
Bt_F23 = np.array([
[ 1.0, 0.0, -1.0, 0.0 ],
[ 0.0, 1.0, 1.0, 0.0 ],
[ 0.0, -1.0, 1.0, 0.0 ],
[ 0.0, 1.0, 0.0, -1.0 ]])
# 输出转换矩阵
At_F23 = np.array([
[ 1.0, 1.0, 1.0, 0.0 ],
[ 0.0, 1.0, -1.0, -1.0 ]])
# 输入矩阵转换 g' = G*g*G转置
def trans_kernel(g):
return np.dot(np.dot(G_F23,g),G_F23.T)
# 权重kernel转换 d' = B转置*d*B转
def trans_input(d):
return np.dot(np.dot(Bt_F23,d),Bt_F23.T)
# o' = g' * d'
# 输出转换 o = A转置*o'*A转
def trans_output(r):
return np.dot(np.dot(At_F23,r),At_F23.T)
def wino_f23(kernel,input):
tran_inp = trans_input(input)
tran_ker = trans_kernel(kernel)
mid = tran_inp * tran_ker
out = trans_output(mid)
return out
def conv_direct(kernel,input):
out=np.zeros((2,2))
for h in range(2):
for w in range(2):
out[h,w]=np.sum(input[h:h+3,w:w+3]*kernel)
return out