【会议记录】基于CPU性能调优的必要性和方法介绍

前一段时间我参加了阅码场的活动 Yomoday(北京场),现场有很多技术专家,非常多前沿的技术分享。这是其中一个主题的记录,尽量还原当时的分享内容,但也可能有些地方不特别准确。

1 基于CPU性能调优的必要性和方法介绍

1.1 会议主讲

朱金鹏,荣耀终端虚拟机和编译优化技术负责人,10+年安卓系统开发经验,致力于安卓手机系统的性能优化。曾就职于华为终端,负责方舟编译器关键特性设计与开发,主导WebAssembly技术在轻量级操作系统上的应用。《简明的TensorFlow2》联合作者,《现代CPU性能分析与优化》译者。

1.2 会议摘要

01 背景信息

  • 译者对《现代CPU性能分析与优化》中文版的历史背景介绍
  • 自我介绍和分享主题介绍,主题是现代 CPU 性能分析与优化
  • 世界日益数据化,数据处理的性能要求愈加强烈
  • 处理器单线程处理性能增长进入瓶颈期
  • 未来计算性能优化的三个维度(软件、算法和硬件)
  • 机会在哪里?软件的不同实现在性能表现上的差异
  • 软件和硬件在性能方面均有能力范围
  • 现在是做软件优化的黄金时期

02 软件性能优化

  • 需要我们怎么做?构建自己的技术栈
  • 避免性能调优的误区,常见的误区包括主观臆断、测试方法、算法估算
  • 定位性能问题的方法,包括代码插桩、火山图、编译器分析报告、TMA 和 Roofline
  • 性能优化的技术点,包括PGO、Vectorization、Memory prefetching、Optimize code layout、Function inlining、Optimize memory accesses,等等
  • 实践是检验真理的唯一标准,通过 perf 或 pmu-tools 进行实战分析

03 TMA分析实例

  • Top-down 微体系结构分析方法
  • TMA分析实例 - 源码
  • TMA分析实例 - 1和2层分布
  • TMA分析实例 - 3层分布
  • TMA分析实例 - 通过perf定位到具体函数
  • TMA分析实例 - 通过__buildin_prefetch内存预取优化

04 建议、推荐和联系

  • 性能分析调优建议
  • 推荐书籍与联系方式

1.3 会议内容

张国强:其实最早看到《现代CPU性能分析与优化》这本书,实际上是宋老师在阅码场强推这本书,这本书非常棒。其实在咱们分享开始之前,我特别想,金鹏分享一下为什么这么执着的非要把这本书翻译出来,还找了那么多出版社,本来就这本开源的书,大家到时候都能下载的到,对吧。你给大家讲一下你当时考虑,后边有没有一些想法或者故事,给大家分享一下。

朱金鹏:其实我在译者序里面已经提到了,现在我就带大家唠叨一下。背景是我到荣耀以后是负责虚拟机的编译器相关的业务,但是我积累的非常少,我也想知道我们哪些业务是需要用编译器来进行优化的,怎么去分析它,需要优化。我们就发现在英特尔平台上,它有 Top-down ,也叫 TMA ,Top-down 是自上而下的一个 CPU 处理结构,也有一个分析方法,是这样一个分析方法。但是这个地方分析方法里面的英文又写得比较晦涩,不是那么直接。我就一直去找相关信息给国内的很多人去讲,但不是特别直接,看到可能也是云里雾里的。非常意外的一个。在阅码场公众号的一篇文章讲这本书,我一看目录,就很关心。读完以后我看下边有评论,这本书要有中文版就好。读完以后感觉非常好,我就尝试翻译一下。是这样一个契机。

01 自我介绍和主题介绍

我先做个自我介绍,我叫陈金鹏,现在是在荣耀终端做虚拟机还有编译优化方向的业务,是我们现在方向技术负责人。之前一直是在我们荣耀华为做手机,目前主要是致力于安卓系统的性能优化,之前还做过一些方舟编译器,还有 WebAssembly 相关的一些技术的探索,我之前在 AI 上也稍微探索一点点,跟其他人写了一本书《简明的TensorFlow2》,而《现代CPU性能分析与优化》这本书是今天主要的介绍。

今天的东西可能会比较轻松哈,不会特别细。给大家讲一下,因为现在硬件按理论来说硬件会越来越好,为什么还要去做这么深的投入和这么多精力去做代码性能调优。然后再举一个例子,怎么去做性能分析?基于 Top-down 性能分析,以及怎么优化它?

02 背景:世界日益数据化,数据处理的性能要求愈加强烈

在这里插入图片描述

首先看,现在整个世界基本上是以数据为中心,数据越来越多,对数据处理的性能要求会越来越强烈。从现在可以看到,整个咱们整个全世界出现的数据大概是从 2020 年已经到了 40CB 。因为它是指数性的增长,今年肯定还会更多。

03 背景:处理器单线程处理性能增长进入瓶颈期

在这里插入图片描述

从硬件上,摩尔定律其实已经进到一个尾声阶段了。大家可以看到图上这一块是晶体管的数量是在慢慢增加的。频率可以看到大家基本上也是慢慢的进入一个持平的状态。比如现在手机上高通可能每年提就挤牙膏似的,跟 Intel 一样挤牙膏挤一点点。功耗它可能现在可能在手机上,功耗会越来越高,手机现在越来越发烫。

但是最关键是单线程性能,我可能核会越来越多,但是单线程性能并没有太增加。我可能会通过手机是小核,中核,大核这样一个策略来给它性能提升,但是对单核来说,它性能并没有特别明显的提升,这是它的一个背景。

04 趋势:未来计算性能优化的三个维度

在这里插入图片描述

这个人叫 John Hennessy,他是哈佛大学的前校长,现在应该和 alphabet (谷歌母公司) 的董事长。他当时做了一个演讲,计算的未来是什么样。他说对于通用的计算,其实现在来说已经死了。

在这里插入图片描述

接下来应该是什么样子?下一对这篇论文就是,应该是 20 年的一篇论文。他讲将来我计算的提升有三个维度,第一个维度就是软件,第二个维度就是算法,第三个维度就是硬件。

我先讲讲硬件,针对硬件的一个精简,或者硬件的专业化,比如处理器更精简,或者基于一个特定的算法,比如 GPU 或者 DSP 的硬件加速器。第二个就是新的一个算法,新的一些算法或者一些机器模型来去推算。

今天咱们的或者这本书,它能解决的问题是什么?可能是通过把我们的软件工程,软件性能工程领域,它里边有两个点。一个就是把软件更精简,再一个就是把软件裁剪的更适合于硬件特性。今天其实这篇这本书里面讲的更多的是这块(软件裁剪),他并没有说我是怎么去改硬件,比如是怎么去设计一个芯片。我怎么基于硬件特征把相同的程序跑得更好?

在后摩尔时期,让你代码跑得更快,会越来越重要。尤其是把它裁剪的或者调优的,更适合于它所运行的硬件是很关键的。

05 机会在哪里?软件的不同实现对性能表现上的差异

在这里插入图片描述

机会在哪里?这个地方它讲了一下,同一个程序我用 Python 去写,用 Java 去写,用 C 去写。普通的写法,它的性能以 Python 为基准,比如 Python 是 1, Java 可能就是11,一 C 就是 47 ,如果是把程序简单做一个强行的并行化是 366,到最终他用硬件本身的一个硬件特性,它有 6万倍(相对于 Python )的增加。这个时间差距还是很大的,如果能把充分的把硬件给利用起来,更充分的去适应具体的芯片,它的能力会提出很多。

06 软件和硬件在性能方面均有能力范围

在这里插入图片描述

其实我们一般都想去依赖于硬件的提升,CPU 提升去提升整体的性能。但是 CPU 它其实是一个死的,它并不说你给我随便也给我一个指令,我都能把你运行。不一定的。对编译器来说,比如 GCC/LLVM 它是利用它的成本或者一个成本收益的模型去估算。但是如果他认为你可能有个优化的或者一个转换,它认为他可能风险比较大的,或者会导致他损耗比较高,或什么时候他就不去做了。他会比较保守,可能就达不到默认的优化的预期。

07 现在是做软件优化的黄金时期

在这里插入图片描述

所以说现在可能就是最好的一个时机去做软件优化,前段时间也有人提过一个黄金十年就是这样一个意思。如果对 Google 搜索增加 2% 的一个速度,可能它的搜索量就会有 2% 的增加。雅虎之前统计过,如果它一个网页加载时间如果增加四百毫秒,可能使用率可能会降 5% 到 9%。

但是整个东西来做,并不是你直接就可以拿到了,需要你深入的去理解你的软件,还有你的硬件怎么去匹配,但是大家有没有做好准备呢?

08 需要我们怎么做?构建自己的技术栈

在这里插入图片描述

这时候需要我们怎么做?可能我们需要构建自己的技术栈。技术栈有哪些呢?就有这些,跟手机一样,手机应用,还有它框架,编译器,OS,还有硬件。可能对咱们这本书里面涉及到的,主要的是这些。

  • 第一个就是编译器,你可能不需要更深入理解编译器的具体的原理,但是你要了解编译器通用的编译优化手段,以及它有比较通用的一些编译优化的选项。
  • 第二个就是 OS 的一个调度,还有一个可能 CPU 绑核。在手机上的话,绑核还是很明显的,如果是在小核上和大核,要是中核,它们也差距很多。硬件的限制。如果你想你的任务要跑特别快,比如假如一个特别重要的前台功能,你需要把你的主线程一个界面相关的线程可能就要绑定到大核上,让他跑这么快。
  • 第三个在硬点上,我可能我们要比较了解 CPU 的微架构是什么样的, CPU 微架构什么样的,为什么我的代码跑的时候它就慢,慢又拆解为几类,怎么去分析它。第二个你要去可能要去尝试的去学习,怎么去读或者改这些汇编的一些指令。

09 避免性能调优的误区

在这里插入图片描述

接下来我们在性能调优的时候会有些误区,这些误区可能有这几类。

  • 第一个就是我看这代码,我自己猜哪个地方可能会有问题,就去改了,结果可能做了一些修改,其实对系统没有任何影响,只是自己在瞎猜。我可能就是猜,但是我并不是知道我具体的数据什么样的,并没有真实去测它,这是很恐怖的。从我们公司内部来看,我们搞问题的肯定是以终为始,一定要是拿到你具体对用户体验相关的一个具体的问题,它是什么样的,拿的数据来,从数据出发。
  • 第二个,可能你自己造了比较糟糕的一个 benchmark 和基准测试,也是非常糟糕。最好是拿比较开源的,大家共同认可的一个。
  • 最后一个,因为都是学计算机相关专业的哈,可能大家比较信仰大O的这种算法的复杂度。但是其实在 CPU 上跑的时候,它并不一定说 O 你算法复杂度越低,它跑得越快,这不一定。

10 定位性能问题的方法

在这里插入图片描述

这本书其实讲了这几种方法。

  • 第一个方法扣的这样的一个插桩,写代码打日志,我看到我函数执行特别多,不是前后加日志。
  • 第二个生成最简单火山图,大家应用也比较多了。
  • 第三个如果不搞编译器可能大家用的比较少。 compare opt reports 就是编译器的一个优化的报告,你可以打开一个编译开关,你在编译的时候把它报告拿出来。这个地方确实我用过一次,但我们是在用过几次,在我们我们公司一个比较关键的特性里边,他就报告里面,我发现有个地方,他可能有一个函数的内联没有做,没做其实它性能就比较差,我们做完以后性能有百分之将近 10% 的收益。这是很明显的。
  • 再一个就是 TMA Top-down,也是书上的例子,待会展开。主要是基于 CPU 架构,应该一个英特尔的,一个以色列的专家提出的构想,对性能的分析来说是非常好。
  • 最后一个是 Roofline 五点线这个模型,CPU 里边性能其实就涉及两类,一个是计算,一个是访存。一个性能与缓存,通过 Roofline 线的来判断,我的当前的程序是在哪个地方。还有再一个就是我硬件当前是什么样一个情况,通过这两个信息我就知道程序有没有充分运用硬件的能力,它的访存,它计算都有没有运用全。所以这个地方是可以用来进行反向的去优化我的硬件设计,也可以来指导我的程序里面怎么去改进,以及是访存的类型,我可能就要去优化访存的一个瓶颈,包括计算,我可能要从计算去交流,但这个目前好像没有看到特别好的工具。作者也并没有讲,只是有这样一个方法。我看到香山开源的 RISC-V 的芯片里面,其实他们相关的研究里面应该也用到了这种方法。

11 性能优化的技术点

在这里插入图片描述

刚才分析完了,我具体评定以后可能有哪些的优化的方法。

  • PGO(Profile Guided Optimization),其实在手机上,在每个任何一个安卓手机上,它其实都在用。用户用的时候它会产生profile,在虚拟机里面的产生 profile 采集用户用的这些函数的次数,它会都会收集起来。例如,等到晚上手机充电的时候,你如果关注晚上充电的时候就摸一下,可能比较烫,一个原因是充电的烫就可能后台做相关的一些任务,就是要基于 profile 进行函数的一个指导编译,因为它全编可能一个是比较大,再一个可能就比较激进。基于 profile 的调用关系,可能更多的可能会把一些函数依赖,或者把一些函数之间的距离可能给它重新排一下,这样它调进的时候,它 attach miss 就会比较小,会好一点。
  • Vectorization 是向量化。
  • Memory prefetching,后边会讲一个 memory prefetching 的例子。
  • Optimize code layout,把你的代码的样子优化一下。
  • Function inlining,函数内联其实是用的挺多的,包括安卓它本身其实在虚拟机的修改,它在 AndroidT 版本的修改,很多都是把函数进行 inline,再进行优化的。刚才提到我们内部也用了,针对一些个别场景,也会把一些函数,尤其 for循环,比如在某一个函数的时候,它都会有调用惯例,这样它会要压栈出栈,这样去还要保存寄存器里的信息,就会很损耗,把它去掉会好很多。
  • Optimize memory accesses,在一些内存的访问的时候,比如让它对齐。
  • 还有一些循环展开,再利用一些技术消除一些分支的预测的信息。

12 实践是检验真理的唯一标准

在这里插入图片描述

提到了一些方法,在书里面其实提到了一个是 perf。其实这还有一个叫 pmu-tools, Vtune 的一个开源版,Vtune 是一个 Intel 开发的一个工具,专门用于 Intel 的 PMU 架构。 pmu-tools 是开源的一个工具。

再补充一下。今天材料主要就是原作者丹尼斯,他在 21 年对他写的当时做的一个材料,主要是也介绍梳理一些内容。我的要闻把材料稍微改了一下,稍微补充一些信息。

13 Top-down 微体系结构分析方法

在这里插入图片描述

这个给大家展示个例子哈。

首先我们看一下主要目的,给大家简单的介绍一下什么是 Top-down。首先我先讲一下 CPU 流水线。 CPU 流水线它其实有这几个阶段,一个指令都它执行,是怎么执行?它有一个 取指-译指-执行-保存-回写,其中前两个阶段是前端,主要是指令相关的,后三个阶段是后端,主要是访存相关的。如果其中有一个阻塞了,可能会导致空转,它的英文叫bound,比如 front and bound 和 back and bound, front and bound 可能是我取值的时候时间长。取指时间原因可能有多种,下面分这几类。

指令一个是从这里面可能开始取过来,再进行分发,再给decode,再往下是执行后端了。执行的时候他可能不同的单元,我们可能要去不同的port,这个 port 里面给他去执行。前后端是这样串起来的。

到于指令执行的时候,我们可以基于它 store 的在哪个阶段给它进行一个分类。第一层分了 4 类。

  • 第一个就是 Retiring ,你就退休了,你寿终就寝就行了。指令已经执行到五个阶段,我都已经执行完了。OK,这是最好的一种状态。如果所有的都是这块,基本上相当于就已经自由了。完全利用了 CPU 的整个流水线是非常好,但是基本上是不可能的,基本上不可能。
  • 第二个就是 Bad Speculation,错误的投机。有时候它可能会去提前执行一些指令,比如有个 if-else,我们可能我不知道它是指哪一个,可能要投机去执行了,我先执行,我执行完以后,我先不提交,提交给寄存器处理。这地方会给他进行一个判断,等到你真正的判断执行完了,如果你是对的,OK,我这部分信息我就提交过去,这边就执行完了。如果不对,我觉得你这个特性,我就把它都给它清掉,CPU 就在这里边执行的流水线执行,相当于这段时间这份资源就浪费了,这一部分也会造成一定损失,这个也是希望它小的。
  • 第三个就是 Frontend Bound,刚提到这个取指的时候可能会慢。
  • 第四个叫 Backend Bound,一般比较多的。

然后再往下一层。

  • Retiring 里面,可能还有一特殊的,到时候看书的可能也会提到,一般浮点可能会有问题。
  • Bad Speculation 一般也他们很难去优化它,可能也会有些困难。
  • Frontend Bound 主要是两类,取指令它有两种,一个是延迟,一种是带宽。带宽大家可以看到带宽过来,因为带宽特别窄,我可能取特别多。带宽特别窄的时候我可能就取不过来了,取不过来我就等带宽;延迟的话,我可能我取个指令的时候,可能我代码编译的可能特别大,比如他是刚执行的 A ,执行 B 并没有在这里挨着,可能特别远。这时候我可能要去从我的内存或者那地方再把它倒过来,那时候就会遇到 iTLB Miss 或者 i-Cache Miss,这时候就会导致等了时间会比较长。
  • Backend Bound 分两类,一个是 Core Bound ,一个是 Memory Bound 。 Core Bound大家简单理解,计算类的,比如我可能我计算特别多,假如加法特别多,或者除法特别多的时候,这时候如果应付不过来,这时候就会产生 Core Bound ,这地方可能会获得时间比较长;最后一个就是 Memory Bound,这可能大家用的会相对可能会多一点。但是 Memory Bound 并不是直接指的内存。里边分好几层,一个是L1/L2/L3 ,还有一个 DRAM Bound ,可能执行过程中我需要读数据,从读数据的时候接着开始读数据,如果在开始, L1 开始有了,OK,那就没问题。最快的 L1 开始没有,随 L2 就会增加一些损耗。最好。如果 L2 没有, 随 L3 再增加一些损耗。如果 L3 也没有,那就得去 DRAM 里边去取,时间会更长。在下面的涉及到 memory 这些带宽,更大的 latency 。

OK,这就是 Top-down 的一个拆解。基于拆解以后,你很明确的可以知道你的程序在 CPU 运行的时候,到底依赖是哪个资源。你回过头来,在不能改变硬件情况下,你怎么去改你的程序,把它更好的利用 CPU 的流水线或者它微架构,让它运行更快。

14 TMA分析实例 - 源码

在这里插入图片描述

这个实例也比较简单,我 malloc 一块内存,这内存是 200 M 的,每个里面都写个 0。我要干什么?我要写一个这么大的循环,应该是 一亿次的一个循环,每个循环我就随机的访问这一刻从 0 到 200M 内存。访问内存,它运行这个时间一般是 8. 5 秒。

怎么去通过用 Top-down 这个方法来进行分析。其实补充一下,在 Linux 上,它英特尔应该都是支持 perf,里面它有个 Top-down 的一个选项,你可以试一下。它可以统计出来一层的我到底是哪种类型的 Bound,这里边用的是 pmu-tools 这个方法。

15 TMA分析实例 - 1和2层分布

在这里插入图片描述

你可以看出来,它程序运行的过程中,它 53% 是在 Backend Bound ,我刚才已经看到 Backend Bound 是在后边的一个。再看第二层,就是 L2 ,在 L2 可以看到,在这里面是 Memory Bound,对 Core Bound 是只有 8.80%,也不多。

16 TMA分析实例 - 3层分布

在这里插入图片描述

再看第三个 L3,L3 的 DRAM Bound,图里边右下角 Bound 时间,整个时间是整个占比47%。你可以看到里面每个 cycle 的时间,如 register 基本上一个 cycle 就搞定了,L1 就可能是 8 ~ 32 cycles ,L2 是 300 cycles ,已经到 memory 了。memory 很多,你越低速度越慢时间越长。

17 TMA分析实例 - 通过perf定位到具体函数

在这里插入图片描述

然后怎么去定位它?有一个地方有对应关系,这个工具理论是英特尔提出来的。英特尔有一个叫 TMA metrics,大家可以在书上可以看到它有对应表格,刚才可以看到它是 DRAM Bound 类型的,它的也需要会给你提供另外一个 L3 miss 的这样一个 PMU 的事件让你去用,你可以去统计 L3 Miss 以后,它就会到 DRAM 那里面去 L3 Miss 事件哪个最多,它通过 perf record 完以后,跟通过他去。可以通过解析审核的 data 文件, prop data 可以看到,这里面主要是复制函数,里面他用到的是 move 的一个缓存的动作,100% 都在这个地方。

18 TMA分析实例 - 通过__buildin_prefetch内存预取优化

在这里插入图片描述

怎么去优化它?在 for 循环里边它不是有一个,刚才代码里面可以看到它是随机的去访问它里边的一个地址。

OK,他现在预计他提前能预取出来。 它有三个参数,第一个参数就是我预取的它的地址是哪一块,把它预取到 cache 里边来。 第二个参数 0 代表是相当于我是读的,第三个参数 1 代表它的程度。第三个参数如果是 0 档基本上我是本地,不是本地的我不取。我认为这块取了以后可能用的不特别多。 1 档代表比较低程度, 2 档代表比较中度的, 3 档代表是高度的。

现在用 __buildin_prefetch ,我理解应该是可以缓解 L3 Miss 这一部分。改完以后效果,这个事件原来应该是比原来相对小了 10 倍。大家可以看一下整个运行性能,提升了 2 秒,原来是 8. 5,基本上也是 30% 的加速这样一个模型。

19 性能分析调优建议

在这里插入图片描述

最后丹尼斯在他书上也反复提了他的优化建议。最后其实他有很多,挑出来这一部分翻译成中文。大家一起来看一下。

  • 第一个默认情况下软件,它并不会到达你最优的一个性能,写完代码以后,你认为可能是有,但是它并不一定非常契合你的硬件,可能你需要还要针对硬件做一些针对性的优化。
  • 刚才也提到,硬件性的自然速度就不如过去几年了,是将来软件性能调优,可能也是提前性能提升性能提升的有关键驱动力。
  • 再一个就是要构建你的技术栈, CPU微体系结构, v 架构,还有阅读汇编代码,还有操作指令测算系统的一些内核的机制,还有一些编译优化的选项等等。
  • 再就是避免性能的误区,一定要去通过测量,通过以实时数据的角度来去指导你性能的一个分析,还有优化
  • 善于使用他们刚才提到的这些工具,进行代码中的一个性能分析,先练习修复他们。

20 推荐书籍与联系方式

在这里插入图片描述

最后感谢大家。大家听自己讲这么多,给大家推荐一下这几本书。一个英文版,一个中文版,这是我个人公众号,大家感兴趣可以关注一下。今天讲的就是这些,谢谢!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hinzer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值