摘要

一个涵盖CPU、GPU、XPU的性能perf工具,在AI以及通用场景下的底层性能调优工具。
就是讲了下是个啥?能做啥?大致怎么做等方面的问题。

前言

  • 之前就一直考虑要做一个移动端的CPU perf工具,不仅能够提供工程级别的perf,还能提供函数级别,甚至是算子级别,代码块级别的perf工具,当时做了个简陋版本,然后就没做下去了,现在刚好接触了NPU,同时也有小伙伴一起做,因此决定要好好的做一下,看是否能为这个AI HPC社区做些什么贡献,大致的初步框架如下:

Arch

下面图片来自知乎大佬KPLIN回答


CPU partial

CPU部分可以参见之前的知乎专栏:


我把内容大致描述如下:

零 · 前言

紧接上次《CPU体系结构之手排流水线专题一》应该是写《CPU体系结构之手排流水线专题0x02》了的,但是考虑到其中我会用到我写的那个PowerPerf工具,因此有必要在这之前简单介绍下我的这个工具:

再说下业内有哪些在用的perf工具,优点以及不足,然后我的这个优点又是啥?

接着,说下我这个是怎么做的,技术架构是啥?

结合实例说明下我这个工具的精度如何?

最后在实际卷积算子的撰写过程中说明这个工具能做到些啥,解决啥问题!


壹 · perf背景介绍

关于perf的介绍,这里有大佬整体介绍过了的:

图片来源: 官网

简单来说就是linux系统在驱动级别就提供了一整套的perf接口,能做很多事!能在应用函数级别perf到很多的数据的呀!比如你看下面能获得各个函数的一些event数据!

linux perf软件(图片来源:google)

火焰图(图片来源:google)

但是缺点是不够灵活,不能做到获取工程中任意某段代码的perf数据

为啥一定要说这是个缺点呢?

我还是啰嗦地说,你看哈,写底层推理框架,通常分了两层,一层就是框架层,一层就是算子层,假如是做一般的优化我们直接用linux的perf软件就ok了的,能得到比较好的算子了,甚至可以得到最优的算子!

但是,我这个人有个毛病,就是做技术要追求最优点,追求全局最优点!既然我们都优化算子了,那么何不把算子的核心“汇编代码块”也优化到极致呢?

你看我们的算子构成是这样的:

operator(){
// code 前处理
// 循环最内层asm汇编部分
//code 后处理
}

我做过大量的实验,在各种算子的实现方式下,“循环最内层asm汇编部分”占了算子总耗时90%+(除了某些特定的算子),因此矛盾的主要点就在这个汇编算子呀!

但是发现问题点没,这部分需要重点优化的就是某段汇编代码,我们需要实现一种灵活方便且相对精确的perf工具来在线perf这段代码块的性能参数!


贰 · perf要得到的参数

如何优化?

循环展开、并行、指令流水重排、cache优化、减少内存复用。。。总共也就这些呀!

但是我们通常就是靠经验来优化一些代码呀,并没有很好的性能参数量化指导方案呀!因此我们可以这么设想:

“要是我写了某一段代码,然后在开发project内编译一跑,就直接得到一组硬件性能参数,并告诉你这段代码只利用了硬件资源的百分之多少,还有多少的上升空间,需要改进的方面有哪哪哪几个方面!”

这样无论是对于理解硬件特性还是优化代码性能,都是一个很不错的设想呀~

嗯就从这个出发,我们可以大概分这么两大类,instruction + cache:

  • 指令流水线这块主要是获取IPC参数,需要的性能参数有指令数、CPU cycles数;
  • cache这块就主要关注:Lx cache hit, Lx cache miss,writeback…..;

要的东西列出来了,那么接下来的问题就是去哪获取的问题了,这个好办,看芯片手册嘛!

ARM PMU function

你看这个模块叫做PMU,你的代码在CPU硬件上跑的过程中就会被这个模块给默默地记录下来了!是硬件级别的记录啊,从我们软件er的角度来说可以认为是实时获取的。

好了知道从哪获取了那我们接下来要考虑的就是如何获取了!

如何获取?我就想到了在前公司使用过的DS-5,当时用它在裸板上调试代码,能看到超多的硬件实时参数信息,我想着我是不是也可以仿照做个阉割版。。。。

直接上官网一看:

里面有段介绍:

得了,这下清楚了,总结下来实现方案就是:

代理:收集数据 → 传输数据;

接下就是好玩的部分了,我们可以凭空想象,想怎么实现就怎么实现,任何技术任何想法,这种“创造”的感觉太TM爽了!咳咳,来,咋们还是再扯扯具体怎么实现吧!


叁 · perf应用框架系统搭建

总之想了几天之后,实现的技术框架就是这个样子,当然里面还是有很多细节的,比如究竟如何获取PMU里面的数据,也是碰到了好多的坑,一步一步才填过来的!这里只做介绍,细节暂且不表,以后有机会再细说实现代码呀!

是吧,思路超简单呀,分两大块上位机显示界面加移动端数据提供端,中间通过自定义的简单通信协议进行数据传递。

最终的实现效果如图所示呀!还是蛮粗糙!但也凑合着用了哇~


肆 · perf工具精度验证

自己写了得验证精度不是,不然咋知道我测的数据准不准啊!

反正一顿操作得到一组数据:

实测数据及结论

理论访存次数 = 4000000
CPU_CYCLES = 341089056
L1D_CACHE_REFILL = 4014193
L1D_CACHE = 4000188
LD_RETIRED = 4000120
Average latency = CPU_CYCLES/LD_RETIRED=85 cycles

L1D_CACHE_REFILL:
就是cache miss数,由于每次访存都miss了,因此,总共是4000000+系统扰动约等于4014193,误差为0.3%;
LD_RETIRED:
访存指令数,理论值为40000000,测试误差为0.03‰;
L1D_CACHE
为CPU实际访问cache的次数,误差为0.05‰;

注意这里我就测了个普通场景的精度,因此在一定的测试环境下精度还是可以的,严苛场景的话就需要我们做一些转换啦~


伍 · 举两个栗子

首先就是举个指令流水的例子,我们的例子是线性访存,如右图所示:

按图所示访存,出现了两个阶梯,第一个阶梯是平均latency为1 cycles,第二个阶梯为2 cycles,第三个就稍微复杂些了,属于cache的范畴,暂且不表,我们来分析下第一二个阶梯的区别及产生的原因;

第一二个阶梯的区别就是一个加了地址偏移0,另外一个没加,因此就是累加地址偏移值的这个操作需要花费一个cycles,从而导致指令耗时增加,我们可以画了流水图看一下:

可以看到理论值跟实测值还是蛮吻合的嘛~

第二个例子就是可以构建出特定硬件的存储器山三维图呀,我们先不看三维就看个二维的,也就是说获取某个特定硬件的L1 latency,L2 latency, memory latency呀~

第三个例子就是在写算子过程中,发现某些特定size下性能的缺陷,通过这个工具可以轻松定位到是cache way conflict的问题呀!(当然,经验丰富的高工前辈们还是能一眼就看出来的呀!向前辈们学习!)


陆 · 总结

总之,这篇文章我实现希望告诉大家我大概做了个啥,能干啥(辅助调优算子)!为下一篇博文做铺垫而已呀! 因此就在这草草搁笔啦!当然我还是希望今后能有机会把这个工具开源出来跟大家一起学习摸索呀!
笔心~

所以当初自己挖的坑,现在终于想起来要填啦,欢迎大家关注呀AI-performance呀!

GPU partial

XPU partial

点击下面找寻彩虹~

🌈 获取中...

-->

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

X-profiler Soft Arch & Design. 上一篇
AI-Performance SPEC. 下一篇