Mecanim 发表于 2022-1-19 09:37

CPU算法性能分析与优化总结

在深度学习等算法部署到工程端时,通常会遇到诸多性能瓶颈问题,这些性能瓶颈通常可以分为CPU、GPU两部分模块来分析,本篇简单总结下CPU端的性能分析与优化经验。首先介绍一些CPU的基础知识,然后是如何分析性能,最后提供一些实际工程中的经验。
CPU基础知识

先介绍下CPU的基本知识,CPU即Central Processing Unit(中央处理单元),主要由运算器、控制器、寄存器组合内部总线组成。
运算器:执行部件,受控制器命令而执行动作,完成各种算数与逻辑运算。
控制器:控制整个CPU的动作,不仅要保证程序正确执行,还要能够处理异常事件。
寄存器:CPU内部的一些小型存储区域,用来暂时存参与运算的数据和运算结果。主要有指令寄存器(IR)、程序计数器(PC)、地址寄存器(AR)、数据寄存器(DR)、累加寄存器(AC)、程序状态字寄存器(PSW)等。
指令集:每款CPU在设计时就规定了一系列与其他硬件电路相配合的指令系统,我们称为指令集(ISA,Instruction Set Architecture),是CPU执行计算任务时遵从的规范。我们写的C++等程序在使用G++等编译器编译后,就会生成指令集可识别的CPU指令。
指令是计算机运行的最小的功能单位,根据指令的复杂程度,一般可以分为复杂指令集和精简指令集两种:
复杂指令集:CISC,指令系统较为复杂,指令字长不固定,更加全能,常见在各种PC中。代表指令集有X86、AVX。
精简指令集:RISC,指令系统简单,指令字长固定,功耗少。代表指令集有ARM(AdvancedRISCMachine),MIPS。ARM家族占了所有32位嵌入式处理器75%的比例。
AVX(Advanced Vector Extension,高级矢量扩展):无论CPU有多快,X86指令也只能单指令单数据。而AVX 聚焦矢量运算,很快形成了一套完整的单指令多数据(SIMD)指令集规范,在2013年,intel发布了AVX-512指令集,指令宽度扩展为 512bit,每个时钟周期内可打包 32 次双精度或 64 次单精度浮点运算。
微架构(Microarchitecture):指令集可以看做是CPU遵守的语言标准,而具体的CPU指令集实现,我们称为微架构。例如i7-4770的核心是Haswell微架构,兼容x86指令集。微架构的设计技术含量极高,业界一般认为只有具备独立微架构研发能力的企业才算具备了CPU研发能力。例如,华为的海思920、三星Exynos 5430,是受ARM授权,由ARM开发的微架构组装成的芯片,只能说是“使用ARM Cortex-A15核心的芯片”;而高通骁龙800、苹果A7是采用了ARM的指令集,微架构则是自主研发的。
多CPU VS 多核:在多并发任务中,多核CPU的并行性能通常要比多CPU好,与之带来的是多核CPU的造价成本高,目前高性能服务器通常会1个CPU会搭载8核、16核等。这是因为每个CPU都需要有自己的电路支持、自己的Cache,他们之间通过板上的总线进行通信,通信代价大,而多核单CPU只需要一套芯片组,一套存储,多核之间通过芯片内部总线进行通信,共享使用内存,通信方便。
多进程与多线程:线程是CPU调度分配的最小单位,进程是CPU分配资源的最小单位。属于同一进程的不同线程会共享进程内存空间中的全局区和堆,而私有的线程空间则主要包括栈和寄存器,对这些共享变量进行访问时,如果要保证线程安全,则必须通过加锁的方式。多进程间通信可以通过socket、pipe、消息队列等实现。多进程的开销较大,但每个进程之间独立,也相对安全些。
缓存(cache):CPU每秒可处理十亿级别指令,而内存一般访问速度为每秒几十兆,如果每次CPU访问数据都去内存中获取,则会受限于内存的速度,因此在CPU中有一种高速存储器缓存,在内存与CPU中充当媒介,加速CPU的数据获取。
CPU缓存通常可分为三级:一级缓存,即L1 Cache,集成在CPU内部中,用于CPU在处理数据过程中数据的暂时保存。一般L1缓存的容量通常在32-256KB。二级缓存,即L2 Cache,由于L1级高速缓存容量的限制,为了再次提高CPU的运算速度,在CPU外部放置一高速存储器,即二级缓存。三级缓存最慢,为所有core共享,大小一般为几十MB。
CPU在读取数据时,先在L1中寻找,再从L2、L3寻找,然后是内存,再后是外存储器。现在普通台式机CPU的L2缓存一般为128KB到2MB或者更高,笔记本、服务器和工作站上用CPU的L2高速缓存。最高可达1MB-3MB制造工艺。现在所使用的CPU制造工艺一般是0.13um、0.09um,随着工艺水平的进入,目前已经提高到64纳米。
X86与ARM主要区别:

性能:X86架构比ARM结构在性能方面要快得多、强得多。X86的CPU主频基本都在1G、2G以上,并伴有双核、四核、八核等,通常使用45nm或更高制程的工艺进行生产;而ARM CPU通常主频更低。从指令集设计思路出发,也可以看出X86强在复杂、综合性任务场景,而在一些任务相对固定的应用场合,ARM的优势就能发挥得淋漓尽致。
兼容扩展性:X86 CPU采用“桥”方式与内存、硬盘等外接设备进行连接,对已有的X86电脑,我们可以很容易增加内存条、硬盘来扩展性能,且由微软、intel组成的wintel联盟垄断了个人电脑长达三十多年,平台的统一就导致了很强的兼容性,有大量兼容的第三方软件可供使用。ARM CPU采用专用数据接口与外设连接,扩展内存、存储较难,奉行“够用就好”,且几乎都采用了Linux系统,硬件平台通常需要自己搭建系统,兼容性较差。
功耗:X86的设计思路是性能与速度,高频多核的设计也使得其功耗一直居高不下。而ARM最为明显的优势就是低功耗,这也使其在手机、各类智能终端、专用设备中拥有大量的应用
<hr/>性能分析:

工欲善其事,必先利其器,在提升程序性能之前,我们首先要利用一系列工具来分析当前CPU、内存的状态,下面介绍几种常用工具:
top:动态展示系统整体资源和各个进程资源占用状况,最为常用;
vmstat:对操作系统的虚拟内存、进程、IO读写、CPU活动等整体情况进行统计;
sar:统计CPU、IO、内存、网卡流量等系统资源的使用信息;
/proc/cpuinfo:在linux系统中,/proc/cpuinfo里提供了CPU的一些基本信息,比如核数、运行频率等,对我们分析CPU状态非常有用。
perf:当我们要进行耗时分析时,除了在程序中打log统计时间,也可以利用perf工具从系统运行维度统计函数与so的耗时占比。Perf 是内置于 Linux 内核源码树中的性能剖析(profiling)工具,原理是每隔固定的时间,在CPU core上产生一个中断,看当前是哪个pid、函数、库在占用core,这样大量采样后命中次数就能代表该函数的时间占比了。
火焰图(flame graph):为了直观分析性能,可以使用火焰图对perf采样到的数据进行归纳合并,形成调用栈的热点柱状图,如下图所示,这样哪些函数、库耗时较大就一目了然了。


<hr/>优化方法:

1:睿频(turbo boost)
当程序性能遇到瓶颈时,首先可以分析下CPU当前的运行频率是否达到最大。Intel从2008年提出了睿频技术,能够动态调整CPU频率,在运行复杂任务时可以实现高于固定频率的睿频,对性能提升还是较为显著的。同时,CPU为了动态节能,也提供了如下图所示的很多种运行模式,将运行模式改为performance或者ondemand,能够极大提升程序性能:
#查看当前运行模式
cat /proc/cpuinfo | grep "model name\|MHz" | head -n 2      
# 配置运行模式为performance
echo "performance" | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

2:提升缓存命中率、绑核
由于缓存指令和数据与CPU同频工作,L1级高速缓存的容量越大,存储的信息越多,可减少CPU与内存之间的数据交换次数,提高CPU的运算效率。
而在多核CPU上,进程有可能在多个核上来回切换运行,这样会大量使用L3缓存(多核共享);而如果能将某一进程绑定到固定的核上,其L1、L2缓存的命中率就会显著提高。
在linux系统里,可以使用taskset命令来将程序绑定到固定core上:
taskset -c 3 command    # 将command启动的进程绑定到core 3上当然,在代码中,尽可能不跳跃的访问连续内存数据,也可以提升缓存命中率。这是因为我们在访问某一个元素时,通常会把该元素前后多个数据一起load进缓存,这样如果连续访问数据的话,就能较多的命中缓存中的数据。
3:IPP加速opencv
当前计算机视觉的算法几乎离不开opencv,如果遇到了opencv算子的性能瓶颈,不妨尝试下IPP来加速opencv。IPP是Intel的集成性能基元库(Integrated Performance Primitives),opencv也曾由IPP的团队主持,多个主要开发者与IPP团队保持着不错的关系,OpenCV利用了IPP高度手工优化的代码来实现加速,使用IPP获得的提速是非常显著的。
OpenCV使用优化了的C和C++代码实现。它对IPP不存在任何依赖。但如果安装了IPP,那么OpenCV将会通过自动载入IPP动态链接库来获取IPP的优势,来提升速度。
4:硬件加速库:MKL(Math Kernel Library)
MKL是Intel推出的数据加速库,使用了OpenMP*实现了线程化,该算法库能够平均的分配数据和任务,充分利用CPU的core和处理器。
对于fft等矩阵运算操作,可以尝试使用MKL库来加速,很多情况下还是会比openblas、eigen性能好一些。
5:指令集AVX-512
在图像处理、数据分析领域中追求高性能时,如果没有GPU可用,不妨尝试下使用AVX-512指令集来进行算法的优化加速。
<hr/>下篇会介绍些GPU端的性能优化方法~
有不严谨的地方还请批评指正~
reference:

Mecanim 发表于 2022-1-19 09:39

请问有了解过arm map, DS-5这类的profile工具吗
页: [1]
查看完整版本: CPU算法性能分析与优化总结