【系列三:DevKit性能分析工具】第二讲:手把手带你找出程序中加锁范围不合理的代码

举报
Tianyi_Li 发表于 2022/11/27 23:02:24 2022/11/27
【摘要】 【系列二:DevKit性能分析工具】第二讲:手把手带你找出程序中加锁范围不合理的代码 如何学习性能优化 性能指标是什么?高并发”和“响应快”一定是最先出现在你脑海里的两个词,而它们也正对应着性能优化的两个核心指标-“吞吐”和“延时”这两个指标是从应用负载的视角来考察性能,直接影响了产品终端的用户体验。跟它们对应的,是从系统资源的视角出发的指标,比如资源使用率、饱和度等。想要学习好性能分析和...

【系列三:DevKit性能分析工具】第二讲:手把手带你找出程序中加锁范围不合理的代码

如何学习性能优化

性能指标是什么?

高并发”和“响应快”一定是最先出现在你脑海里的两个词,而它们也正对应着性能优化的两个核心指标-“吞吐”和“延时”这两个指标是从应用负载的视角来考察性能,直接影响了产品终端的用户体验。跟它们对应的,是从系统资源的视角出发的指标,比如资源使用率、饱和度等。

想要学习好性能分析和优化,建立整体系统性能的全局观是最核心的话题。因而:

  • 理解最基本的几个系统知识原理 (cpu、内存、存储l0、网络IO) ;
  • 掌握必要的性能工具;
  • 通过实际的场景演练,贯穿不同的组件。

如何学习性能优化?

其实说到性能工具,就不得不提性能领域的大师布伦丹·格雷格(Brendan Gregg)他不又是动态追踪工具DTrace的作者,还开发了许许多多的性能工具。我相信你一定见过他所描绘的 Linux性能工具图谱(其实,在上一讲已经展示过了,哈哈):

image.png

安装DevKit性能分析工具

image.png
image.png

调优前的准备工作

因为我们使用的是远程实验室的资源,已经安装好了一切,就直接用就好了。

操作流程

  1. 首先,登录鲲鹏性能分析工具,登录界面如下图所示:

image.png

登录成功后,可看到如下四个界面:

image.png

可以看到,鲲鹏性能分析工具由四个子工具组成,分别为:系统性能分析、Java性能分析、系统诊断和调优助手:

系统性能分析

在软件运行状态下,通过采集系统数据,可视化分析出系统性能指标,精准定位到瓶颈点及热点函数,提供一站式分析报告、多维度数据关联及优化建议。

Java性能分析

针对服务器上运行的Java程序,图形化显示Java程序的堆、线程、锁、垃圾回收等信息,收集热点函数,定位性能瓶颈点,帮助用户采取针对性优化。

系统诊断

针对基于鲲鹏的服务器的性能分析工具,提供内存泄漏诊断(包括内存未释放和异常释放)、内存越界诊断、内存消耗信息分析展示、OOM诊断能力,帮助用户识别出源代码中内存使用的问题点,提升程序的可靠性;压测网络,获得网络最大能力,为网络IO性能优化提供基础参考数据;诊断网络,定位网络疑难问题,解决因网络配置和异常而导致的网络IO性能问题;压测存储IO,获得存储设备最大能力,包括:吞吐量、IOPS、时延等,并以此评估存储能力,为存储IO性能优化提供基础参考数据。

调优助手

针对基于鲲鹏的服务器的调优工具,能系统化组织性能指标,引导用户分析性能瓶颈,实现快速调优。

  1. 新建分析任务

该界面如下图所示,可根据我们实际需要创建想要的分析任务:

image.png

可以看到这里有8个分析类型,具体选择要综合全局考虑。如果不知道当前程序瓶颈所在,就可以先使用通用分析来找症结,之后就可以针对性选择对应的系统部件分析和专项分析了。

  1. 全景分析任务

下面介绍下全景分析任务,首先接入总览界面,在这里,会展示具体的各种运行和性能的信息,还会给出优化建议和修改方法,可以按照建议试试,如下图所示:

image.png

下面是PCle拓扑信息,如果是物理机,会显示地更加详细和具体地信息:

image.png

接下来是性能,如图所示,可以很详细地展示四大组件的相关信息,支持表格和图格式展示,非常方便,我们主要看系统态和内核态使用情况、IO、软中断和硬中断的使用情况。

image.png

以上只是很粗略的分析,因为这里的信息很多,所以实际要针对具体任务进行分析,并不断尝试,需要付出一定努力。

下面,我们主要介绍下热点函数分析。采用热点函数分析,确定哪些热点函数存在性能瓶颈。主要介绍以下几点问题:

IDR指令比例高

CPU将内存中的数据读到CPU的高速缓存cache时,除了读取本次要访问的数据,还会预取本次数据的周边数据到cache里面,如果预取的数据是下次要访问的数据,那么性能会提升;如果预取的数据不是下次要取的数据,那么会浪费内存带宽。

对于数据比较集中的场景,预取的命中率高,适合打开CPU预取;若数据不集中,预取命中率低,则浪费内存带宽。

开启CPU Prefetching.

内联函数

频繁调用的小函数优化成内联函数,内联函数是典型的空间换时间的优化方法。参考链接:https://www.hikunpeng.com/document/detail/zh/kunpengdevps/hypertuner/hypertuner-tuner/kunpengtuning_12_0085.html

NEON指令

采用NEON指令(向量化)优化。参考链接:https://www.hikunpeng.com/document/detail/zh/kunpengdevps/hypertuner/hypertuner-tuner/kunpengtuning_12_0053.html

循环优化

循环优化是对程序中使用到的循环部分进行代码优化,合理的优化可以充分利用处理器的计算单元,提升指令流水线的调度效率,也可以提升cache命中率。循环优化的方法有很多,如循环展开、循环融合、循环分离、循环交换和循环平铺。

  • 循环展开

循环展开(loop unrolling)牺牲函数的尺寸,降低循环带来的开销,加快程序执行速度,是一种典型的空间换时间方法。

针对Kunpeng 920处理器具有多个功能单元(FSUx2,ALUx3,LD/STx2)的特点,循环展开也有利于充分使用功能单元进行指令级并行,优化指令流水线的调度。

使用方法:针对循环之间不存在循环依赖或访问冲突的场景,可以使用SIMD改写、手工循环展开或者在编译选项中添加-funroll-loops(该选项是针对所有循环,建议只对热点函数手工循环展开);针对循环之间存在依赖或访问冲突的场景,可以考虑手工循环展开的方式,减少循环开销。

特别说明:小循环内部没有判断逻辑,收益较高; 大循环展开有可能会引起通用寄存器的溢出,降低性能(寄存器重命名/外溢至内存); 内部有判断逻辑可能会增加分支预测的开销,需要具体情况具体分析。

  • 循环融合

循环融合(loop fusion)对于小循环体,可以考虑将函数上下流中的多个循环合并至一个循环内执行,减少对循环变量的操作;合并小循环后也有利于增加Kunpeng处理器乱序执行的机会,提升指令级的并行能力。

  • 循环分离

循环分离(loop splitting) 针对较为复杂或计算密集的循环,可以将大循环拆分至多个小循环内执行,可以提升寄存器的利用效率;拆分大循环与合并小循环的使用是相对概念,难以绝对而言。 拆分成两个循环后,IPC得到了提升。

  • 循环置换

循环置换(loop permutation) 在C语言中,多维数组的数据是按行存储的,在访问下列数据结构的数组元素时,使用循环置换改变顺序,可以有效减少时间。

  • 循环平铺

循环平铺(loop tiling) 将一个循环拆分成一组嵌套循环,每个内部循环负责一个小数据块。下图是一个矩阵乘法的代码,通过将矩阵分为大小为blk*blk的小矩阵,在内层先做小矩阵的乘法,外层做分块矩阵的乘法,可以更好地提升cache利用率。参考链接:https://www.hikunpeng.com/document/detail/zh/kunpengdevps/hypertuner/hypertuner-tuner/kunpengtuning_12_0083.html

Cache优化

  • cacheline对齐

参考链接:https://www.hikunpeng.com/document/detail/zh/kunpengdevps/hypertuner/hypertuner-tuner/kunpengtuning_12_0052.html

  • 消除伪共享

伪共享是指多核的多个私有变量位于同一个cacheline内,由于每个核修改变量时,会将其它核的整个cacheline无效掉,这样就会造成该cacheline在不同的核频繁迁移。这种现象类似共享变量的读写,但又不是真正的共享变量,故称伪共享。如图为伪共享所示。

image.png

如上图所示,CPU0与CPU1的私有变量刚好位于同一个cacheline内(私有变量分别对应红色块与蓝色块),CPU0修改其私有变量,会将整个cacheline无效,CPU1要访问其私有变量又需要重新从内存中读取,效率降低。可采用如下优化方案:

OpenMP代码中使用reduction子句替代直接写入共享变量(循环过程中写入线程私有变量)。

线程私有的变量按照cacheline大小对齐(线程栈上的变量除外)。

使用线程私有变量(如GCC支持__thread,C11支持_Thread_local关键字)。

多核优化之减少跨NUMA访问

鲲鹏处理器为乐高架构,每个处理器Socket内部拥有两个NUMA Node。线程/进程实际运行所在的物理核与内存NUMA Node位置关系,会带来访存路径时延的差异,本地访问性能最佳,本Socket内跨一次NUMA次之,跨Socket访问性能最不理想。

NUMA节点配置如下图所示:

image.png

可采用优化方案:

  • 避免线程在运行过程中迁移:使用OpenMP时,通过配置环境变量OMP_PROC_BIND=true及OMP_PLACES指定线程要绑定的CPU核。如下图所示。

image.png

  • 用numactl工具、taskset、cgroup/cpuset工具绑定进程/线程的位置关系。具体如下图所示。

image.png

其实,还有很多很多,很难一一展开。这都要求开发者对硬件架构、特性,操作系统和软件有较好的整体大局观,对于细节又要很了解,所以整体来说要求还是较高的,这也充分说明软硬协同优化的重要性啊。更多内容可到鲲鹏社区查看开发者文档。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。