目标:Darknet 源码cpu矩阵乘法函数 gemm_nn 优化。参数说明:lda A的列数; ldb B的列数; ldc C的列数; M C的行数; K A的列数

测试方法:Darknet源码,makefile文件添加编译选项 -pg,编译后得到可执行程序 darknet,运行可执行程序:

./darknet detect cfg/yolov3-tiny.cfg yolov3-tiny.weights data/kite.jpg

得到文件 gmon.out,转换为文本,查看热点函数及其耗时。

gprof darknet gmon.out > out
  1. 矩阵乘法基础写法
for(int i=0; i<M; i++)//矩阵A: M*K
{
for(int j=o; j<N; j++)//矩阵B: K*N
{
for(int k=0; k<K; k++)
{
C[i][j] += A[i][k] * B[k][j];
}
}
}
  1. darknet 矩阵乘法源码 src/gemm.c
void gemm_nn(int M, int N, int K, float ALPHA, float *A, int lda, float*B, int ldb,loat *C, int ldc)
{
int i,j,k;
for (i = 0; i < M; ++i)//矩阵A: M*K
{
for (j = 0; j < N; ++j)//矩阵B: K*N
{
register float c_sum = 0.0f;//使用局部变量减少对传入内存引用
for (k = 0; k < K; ++k)//K 矩阵A列数;按行列顺序,逐一完成C 元素的计算
{
//lda 矩阵A的列数;lAb 矩阵B的列数
c_sum += ALPHA * A[i * lda + k] * B[k * ldb + j ];
}
//ldc 矩阵C的列数
C[i * ldc +j] += c_sum;
}
}
}
  1. 循环展开优化:2x和4x加速不明显
void gemm_nn(int M, int N, int K, float ALPHA,
float *A, int lda,
float *B, int ldb,
float *C, int ldc)
{
int i,j,k;
for (i = 0; i < M; ++i)
{
for (j = 0; j < N; ++j)
{
register float c_sum = 0.0f;
#if 0
for (k = 0; k < K; ++k)//一个完整元素的完整计算过程
{
c_sum += ALPHA * A[i * lda + k] * B[k * ldb + j];
}
#elif 1 //2x
for (k = 0; k < K; k += 2)//一个完整元素的完整计算过程
{
c_sum += ALPHA * A[i * lda + k] * B[k * ldb + j];
c_sum += ALPHA * A[i * lda + k+1] * B[(k+1) * ldb + j];
}
#elif //4x
for (k = 0; k < K; k += 4)//一个完整元素的完整计算过程
{
c_sum += ALPHA * A[i * lda + k] * B[k * ldb + j];
c_sum += ALPHA * A[i * lda + k+1] * B[(k+1) * ldb + j];
c_sum += ALPHA * A[i * lda + k+2] * B[(k+2) * ldb + j];
c_sum += ALPHA * A[i * lda + k+3] * B[(k+3) * ldb + j];
}
#elif
#endif
C[i * ldc + j] += c_sum;
}
}
}
  1. 矩阵乘法一维分块优化
void gemm_nn(int M, int N, int K, float ALPHA,
float *A, int lda,
float *B, int ldb,
float *C, int ldc)
{
memset(C,0,sizeof(float)*ldc*M);
for(int m=0; m<M; m += 4)//矩阵A M*K 行方向遍历,定位块行号
{
for(int n=0; n<N; n += 4)//矩阵B K*N 列方向遍历,定位块列号
{
for(int k=0; k<K; k++)//K方向遍历,放在最内层也可
{
for(int mb=0; mb<4; mb++)//定位块内行号
{
for(int nb=0; nb<4; nb++)//定位快内列号
{
C[(m+mb)*ldc + (n+nb)] += ALPHA* A[(m+mb)*lda + (k)]*B[(k)*ldb + (n+nb)];
}
}
}
}
}
}
  1. gemm_nn 矩阵乘法二维分块优化:cannon 算法
void gemm_nn(int M, int N, int K, float ALPHA,
float *A, int lda,
float *B, int ldb,
float *C, int ldc)
{
memset(C,0,sizeof(float)*ldc*M);
for(int m=0; m<M; m += 4)//矩阵A M*K M方向遍历,定位块行号
{
for(int n=0; n<N; n += 4)//矩阵B K*N N方向遍历,定位块列号
{
for(int k=0; k<K; k += 4)//K方向遍历,定位块号
{
for(int mb=0; mb<4; mb++)//定位块内行号
{
for(int nb=0; nb<4; nb++)//定位块内列号
{
for(int kb=0; kb<4; kb++)//快内按照 K 方向累加
{
C[(m+mb)*ldc + (n+nb)] = ALPHA * A [(m+mb)*lda + (k+kb)] * B[(k+kb)*ldb + (n+nb)];
}
}
}
}
}
}
}
  1. 对B转置计算

    通常矩阵元素的计算公式:

    C [ m ] [ n ] = ∑ 0 k − 1 A [ m ] [ k ] ∗ B [ k ] [ n ]

    优化后的矩阵元素计算公式:

    C [ m ] [ n ] = ∑ 0 k − 1 A [ m ] [ k ] ∗ B T [ n ] [ k ]

    代码实现:
void gemm_nn(int M, int N, int K, float ALPHA, float *A, int lda, float*B, int ldb,loat *C, int ldc)
{
//对B转置再计算,可以增加缓存命中
memset(C,0,sizeof(float)*ldc*M);
float* Btemp=calloc(K*ldb,sizeof(float));
for(int i=0;i<K;i++)
{
for(int j=0;j<ldb;j++)
Btemp[j*K+i] = B[i*ldb+j];
}
for(int m=0;m<M;m++)
{
for(int n=0;n<ldb;n++)
{
for(int k=0;k<K;k++)
C[m*ldc+n] += ALPHA * A[m*lda+k] * Btemp[n*K+k];
}
}
free(Btemp);
}
  1. 测试结果
gemm_nn 函数耗时:
darknet 源码 11.7s
loop 2x循环展开:11.5s
loop 4x循环展开:12.1s
一维分块4*K:9.2,比源码提升 27%
二维分块block 4*4 :6.53s,比源码效率提升 79%
对B转置:5.17s,比源码提升 126%

高性能计算-gemm串行计算优化(3)的更多相关文章

  1. 百度云BaaS体系揭秘,突破共识机制、单机计算和串行处理三大瓶颈

    区块链作为去中心化的技术机制拥有广泛的应用场景与市场潜能.自2017年爆发式增长后,区块链虽然已经进入平稳期,但仍然存在概念混淆.技术性能制约.智能合约制约.共识机制.网络建设等痛点.为了打破行业壁垒 ...

  2. 痞子衡嵌入式:了解i.MXRT1060系列ROM中串行NOR Flash启动初始化流程优化点

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是i.MXRT1060系列ROM中串行NOR Flash启动初始化流程优化点. 前段时间痞子衡写了一篇 <深入i.MXRT1050系 ...

  3. ForkJoin、并行流计算、串行流计算对比

    ForkJoin 什么是 ForkJoin ForkJoin 是一个把大任务拆分为多个小任务来分别计算的并行计算框架 ForkJoin 特点:工作窃取 这里面维护的都是双端队列,因此但其中一个线程完成 ...

  4. CNN卷积神经网络_深度残差网络 ResNet——解决神经网络过深反而引起误差增加的根本问题,Highway NetWork 则允许保留一定比例的原始输入 x。(这种思想在inception模型也有,例如卷积是concat并行,而不是串行)这样前面一层的信息,有一定比例可以不经过矩阵乘法和非线性变换,直接传输到下一层,仿佛一条信息高速公路,因此得名Highway Network

    from:https://blog.csdn.net/diamonjoy_zone/article/details/70904212 环境:Win8.1 TensorFlow1.0.1 软件:Anac ...

  5. CUDA-F-1-0-并行计算与计算机架构

    Abstract: 本文从总体上给出了CUDA编程的Big picture,后续所有的文章都在本文的基础上详细展开. Keywords: 并行计算,串行编程,并行编程,计算机架构,并行性,异构架构,C ...

  6. 【Java8新特性】关于并行流与串行流,你必须掌握这些!!

    写在前面 提到Java8,我们不得不说的就是Lambda表达式和Stream API.而在Java8中,对于并行流和串行流同样做了大量的优化.对于并行流和串行流的知识,也是在面试过程中,经常被问到的知 ...

  7. Java8的新特性--并行流与串行流

    目录 写在前面 Fork/Join框架 Fork/Join框架与传统线程池的区别 传统的线程池 Fork/Join框架 Fork/Join框架的使用 Java8中的并行流 写在前面 我们都知道,在开发 ...

  8. GEMM与AutoKernel算子优化

    GEMM与AutoKernel算子优化 随着AI技术的快速发展,深度学习在各个领域得到了广泛应用.深度学习模型能否成功在终端落地应用,满足产品需求,一个关键的指标就是神经网络模型的推理性能.一大波算法 ...

  9. 串行移位锁存并行输出可级联器件74HC595

    一.背景 老同学今天突然咨询关于74HC595,自己没用过,同学说可以级联10级!10级?我艹,这么叼,级联又是 什么鬼,这勾起了我极大兴趣,二话不说,手册down下来研究,并在此做个记录. 二.正文 ...

  10. STM32学习笔记(五) USART异步串行口输入输出(轮询模式)

    学习是一个简单的过程,只要有善于发掘的眼睛,总能学到新知识,然而如何坚持不懈的学习却很困难,对我亦如此,生活中有太多的诱惑,最后只想说一句勿忘初心.闲话不多扯,本篇讲诉的是异步串行口的输入输出,串口在 ...

随机推荐

  1. 实用接地气的 .NET 微服务框架

    前言 微服务架构已经成为搭建高效.可扩展系统的关键技术之一,然而,现有许多微服务框架往往过于复杂,使得我们普通开发者难以快速上手并体验到微服务带了的便利.为了解决这一问题,于是作者精心打造了一款最接地 ...

  2. draw.io 使用自定义字体

    draw.io 是我最常用的作图工具.然而,draw.io 支持的字体很少,只支持少数英文字体.不过好在 draw.io 提供了自定义选项,我们可以填入自己想要使用的字体的名称,然后 draw.io ...

  3. 微信小程序--云开发支付闭环

    云开发支付流程闭环 extends 微信小程序--使用云开发完成支付闭环 在上述文章中,我们对支付结果的处理更多依赖于小程序端的操作 订单号存储在小程序端 支付结果采用小程序端定时触发器轮询 现在我对 ...

  4. 【笔记】Tapable源码解析图以及webpack怎样实现一个插件plugin

    Tapable源码解析图,如图所示: 一个webpack plugin由一下几个步骤组成: 一个JavaScript类函数. 在函数原型 (prototype)中定义一个注入compiler对象的ap ...

  5. 小tips:HTML的实体

    为了能在HTML文档中正确显示某些特殊字符,就需要使用HTML实体(entity).HTML实体就是对当前文档的编码方式不能包含的字符,提供一种转义表示. HTML实体定义 1.名称方式 名称方式会以 ...

  6. 小tips: HTTP 请求过程示例图及名称解释

    一个 HTTP 请求过程示例图: 一个 HTTP 请求需要经历以上过程,接下来看一个具体的例子: 名词解释: Queueing: 在请求队列中的时间. Stalled: 从TCP 连接建立完成,到真正 ...

  7. Identity – Custom Entity

    扩展属性 Custom Entity 指的是我们想对 Identity 的几个 Entity 做修改. 比如 User 要多一些 property, 或者 Id 用 int 而不是默认的 GUID. ...

  8. Unity中的三种渲染路径

    Unity中的渲染路径 Unity的渲染路径 在Unity里,渲染路径(Rendering Path)决定了光照是如何应用到Unity Shader中的.因此,我们只有为Shader正确地选择和设置了 ...

  9. QQ或者微信可以放昵称的超好看的符号

    ☪︎⋆ ✯ ⛈ •ᴗ• •ᴥ• ◔.̮◔ ᕱ ᕱ ⸝⸝· ᴥ ·⸝⸝ ʕ·͡ˑ·ཻʔ ʕ•̫͡•ོʔ ˃̣̣̥᷄⌓˂̣̣̥᷅ °꒰'ꀾ'꒱° ⋆ᶿ̵᷄ ˒̼ ᶿ̵᷅⋆ ˙ϖ˙ ⚝ ︎ .˗ˏˋ♡ˎˊ˗ ...

  10. 配置 ZRAM,实现 Linux 下的内存压缩,零成本低开销获得成倍内存扩增

    由于项目需求,笔者最近在一台 Linux 服务器上部署了 ElasticSearch 集群,却发现运行过程中经常出现查询速度突然降低的问题,登录服务器后发现是物理内存不足,导致机器频繁发生页面交换.由 ...