《锁长期等待》项目大作业解析
《锁长期等待》项目大作业解析
一、作业题目
通过性能分析工具找出程序中加锁范围不合理的地方并解决。
多线程程序,经常存在锁不合理长期占用的问题。这次主要针对这一问题,通过鲲鹏性能分析工具资源调度功能来观测,建议在分析业务逻辑将不需要锁的计算移出互斥量加锁操作。
二、操作前提
1)认真观看迁移工具的实战视频。
2)在鲲鹏社区申请远程实验室,操作系统选择OpenEuler。
https://www.hikunpeng.com/zh/developer/devkit
3)这里选择远程服务器,具体配置和信息如下图所示。
申请之后,会收到一封邮件,里面会有详细的环境配置信息。
三、准备工作
1. 登录服务器,并准备代码
a. 使用MobaXterm工具,以root用户登录服务器。
b. 将文件pthread_mutex_long.c上传到home目录,并赋予所有用户只读、只写、可执行权限。
pthread_mutex_long.c可从如下GitHub仓库获取:https://github.com/kunpengcompute/devkitdemo/tree/main/Hyper_tuner/testdemo/lock
赋予所有用户只读、只写、可执行权限命令如下:
chmod 777 pthread_mutex_long.c
c.编译pthread_mutex_long.c并赋予执行文件所有用户只读、只写、可执行权限
gcc -g pthread_mutex_long.c -o pthread_mutex_long -lpthread -lm && chmod 777 pthread_mutex_long
d.绑核到CPU 0,1,使用后台运行程序,需要确定0-1核上没有其他程序正在绑核运行。nohup命令使得即使退出账户之后会继续运行相应的进程,防止任务中断。
nohup taskset -c 0-1 ./pthread_mutex_long >>pthread_mutex_long.out 2>&1 &
程序运行的输出(标准输出(1))将会保存到pthread_mutex_long.out文件,错误信息(2)会重定向到pthread_mutex_long.out文件。
四、操作步骤截图
1. 登录鲲鹏性能分析工具Web界面。
具体登录信息可在邮件中查看,注意需要登录VPN。
登录后,自动跳转到如下界面:
2. 利用鲲鹏性能分析工具创建系统全景分析任务,分析当前程序
选择系统性能分析,首先要新建一个工程,具体如下图:
a. 创建系统全景分析任务,启动分析。
具体配置如上图所示。点击开始分析,分析完成后,会进入如下界面:
如下为一些具体的截图:
系统全景分析结果总览:
系统全景性能分析结果(系统性能):
从上图可以看出,当前有CPU核0,1的使用率(%user的数值)偏高,两者相加在100%左右。
系统全景性能分析结果(进程性能):
3. 采用进程/线程性能分析,找到是哪个进程造成的CPU消耗。
a. 创建进程/线程分析任务,并启动分析,具体配置如下图。
b. 查看采集分析结果。
进程/线程分析结果总览截图如下所示,图中默认排序是按照PID/TID升序排列。
为了观察哪个进程造成了CPU消耗,需要点击%CPU列旁边按钮,进行降序排列,如下图所示,可以看到pthread_mutex_l程序在消耗大量的CPU。
3. 采用系统资源调度分析,确定看出pthread_mutex_l程序进程及线程的切换次数及抢占情况。
a.创建资源调度分析任务,并启动分析。
b.查看采集分析结果。
资源调度分析结果总览截图:
在“总览”页签中,点击线程名、PID或者TID旁边的过滤按钮,可以选择想要观察的线程,从以上两个步骤的分析得知pthread_mutex_l程序存在问题,所以我们直接找这个程序所对应的PID,通过PID旁边的过滤按钮,过滤出pthread_mutex_l进程,如下图所示。
在“进程/线程调度”的页签中,可以看到pthread_mutex_l两个线程之间情况。绿色代表当前线程处于运行状态,橙色代表当前线程处于等待状态,观察可以发现pthread_mutex_l两个进程之间存在一个线程长期占用(绿色部分)而另一个线程长期等待状态(橙色部分),而没有平衡的相互交替运行(橙色绿色在两个线程中的长度相等)。如下图所示。将鼠标或者使用触摸板放在调度图上,滑动鼠标滚轮,可以放大、缩小图片,可以更清楚地看到等待运行的分布情况。
由此,我们可以推断两个线程在频繁抢占,最有可能就是有锁等互斥量操作。其中一个线程长期占用锁,导致另一个线程无法得到锁而长期等待。
4. 热点函数分析
a.创建热点函数分析任务,并启动分析。
b.查看采集分析结果。
如下图所示为热点函数分析任务结果-总览。我们可以看到这个模块/home/pthread_mutex_long的时钟周期百分比在88%以上,符合我们的预期。
通过对热点函数的源码分析,发现其中有一段业务逻辑并不涉及并发的场景,并且这块业务逻辑是Func函数中热度最高的,如下图所示,理论上完全可以移到锁的范围之外去。
下面开始优化代码,将不需要互斥的操作移到pthread_mutex_unlock()之后。优化前后对比如下图所示。
好了,代码优化后,我们重新启动验证一下。不过,我们先要结束当前程序,通过jobs -l查看pthread_mutex_long程序运行pid(如果程序尚未结束,可以看到后台运行的程序),通过kill -9结束进程。
jobs -l
kill -9 <pthread_mutex_long程序pid>
操作截图如下:
5. 重新运行程序
a. 重新编译pthread_mutex_long.c并赋予执行文件所有用户只读、只写、可执行权限。
gcc -g pthread_mutex_long.c -o pthread_mutex_long -lpthread -lm && chmod 777 pthread_mutex_long
b.绑核到CPU 0,1,使用后台运行程序,需要确定0-1核上没有其他程序正在绑核运行。
nohup taskset -c 0-1 ./pthread_mutex_long >>pthread_mutex_long.out 2>&1 &
操作截图如下图所示:
c.采用系统资源调度分析。
创建系统资源调度分析任务,并启动运行,如下图所示:
查看分析结果,在“总览”页签中,点击线程名、PID或者TID旁边的过滤按钮,可以选择pthread_mutex_l,可以发现切换次数相较优化前大幅减少。如下图所示。
在“进程/线程调度”页签中,可以看到pthread_mutex_l两个线程之间情况。如下图所示,可以看出,此时,两个线程切换时等待时间很短(橙色部分),这段时间用于计算需要互斥的全局变量,除此之外,两个线程可以并发运行不需要互斥操作的计算(绿色部分),大幅度提升了计算性能。
最后不要忘记结束样例程序。
通过jobs -l查看pthread_mutex_long_mod程序运行pid(如果程序尚未结束,可以看到后台运行的程序),通过kill -9结束进程。具体命令如下:
jobs -l
kill -9 <pthread_mutex_long_mod程序pid>
操作截图如下所示:
- 点赞
- 收藏
- 关注作者
评论(0)