EN
注册
我要评分
文档获取效率
文档正确性
内容完整性
文档易理解
在线提单
论坛求助

示例3:锁频繁抢占

简介

多线程程序,经常会存在锁频繁抢占问题,锁的争抢会导致CPU的资源浪费,可以基于实际的业务减少公共资源的抢占。本示例主要针对这一问题,通过鲲鹏DevKit系统性能分析工具资源调度和锁与等待分析功能来观测,建议用户分析业务逻辑减少锁的争抢,例如大锁变小锁,减少线程并发数等。

环境准备

  1. 请确认服务器上已安装的操作系统是否兼容,GCC版本是否为7.3.0及以上,兼容性请参见鲲鹏DevKit兼容性查询助手
  2. 请确认服务器上是否已安装鲲鹏DevKit系统性能分析工具。
  3. 请从Github下载代码样例,执行以下命令赋予所有用户可读、可写和可执行权限。

    样例代码文件为:pthread_atomic.c、pthread_mutex.c、pthread_mutex_long.c和pthread_mutex_long_mod.c。

    chmod 777 pthread_mutex.c pthread_atomic.c pthread_mutex_long.c pthread_mutex_long_mod.c

性能分析过程

  1. 程序准备。
    1. 编译pthread_mutex.c并赋予执行文件所有用户可读、可写和可执行权限。
      gcc -g pthread_mutex.c -o pthread_mutex -lpthread && chmod 777 pthread_mutex
      • demo要求GCC版本在7.3以上,若需要关联源码,建议编译时加上-g参数,如:gcc -g pthread_mutex.c -o pthread_mutex -lpthread。
      • 若使用的GCC版本为10.x.x及以上,因该版本默认启用LSE指令集,可能优化效果不明显,建议编译程序时添加参数“-march=armv8-a+nolse -mno-outline-atomics”用以禁用该指令集。
      • 本实践除了锁与等待的分析对象为应用级外,其他模块均为系统级调优,进程和资源调度任务为了分析锁抢占情况,锁与等待任务定位优化锁抢占。
    2. 后台运行程序,nohup命令使得即使退出账户之后会继续运行相应的进程,防止任务中断。
      nohup taskset -c 0-1 ./pthread_mutex >>pthread_mutex.out 2>&1 &

      程序运行的输出(标准输出(1))将会保存到pthread_mutex.out文件,错误信息(2)会重定向到pthread_mutex.out文件。

      该程序一般运行时间为20秒左右,若已运行完成,启动采集任务将无法采集到该程序数据,可修改pthread_mutex.c源码中count参数增加对应运行时间或运行该程序后立即启动采集任务。

    3. 优化后程序pthread_atomic.c使用1.a一致的编译方式。
  2. 采用进程/线程性能分析,查看造成CPU消耗的进程。

    创建进程/线程分析任务,并启动分析。

    表1 任务参数配置说明

    参数

    说明

    分析类型

    进程/线程性能分析

    分析对象

    系统

    采样时长

    10秒

    采样类型

    全选

    其他参数

    默认

  3. 查看采集分析结果。
    图1 进程/线程分析结果总览

    默认排序是按照PID/TID升序排列,为便于观察CPU消耗的进程详情,请点击%CPU列旁边的按钮进行降序排列,可发现pthread_mutex程序在消耗大量的CPU资源,实际结果将根据实际运行情况显示,同图1略有差别。

  4. 选择“上下文切换”页签,点击筛选按钮选择pthread_mutex程序所对应的PID。
    图2 上下文切换分析结果

    发现pthread_mutex对应进程及线程的上下文切换比较多,可推测该进程存在较多的切换。

  5. 采用系统资源调度分析,查看pthread_mutex程序进程和线程的切换次数及抢占情况。

    创建资源调度分析任务,并启动分析。

    表2 任务参数配置说明

    参数

    说明

    分析对象

    应用

    模式

    选择“Launch application”

    分析类型

    资源调度分析

    应用路径

    输入示例应用路径,如:/opt/testdemo/pthread_mutex。

    采样时长

    30秒

    其他参数

    默认

  6. 查看“CPU/进程统计”页签。
    图3 CPU/进程统计

    可观察到pthread_mutex两个线程之间存在频繁交替抢占的情况,此情况最大可能是锁等互斥量操作。

  7. 采用锁与等待分析,得到锁的调用情况。

    创建锁与等待分析任务,并启动分析。

    表3 任务配置参数说明

    参数

    说明

    分析对象

    应用

    模式

    Attach to process

    分析类型

    锁与等待分析

    PID

    选择pthread_mutex应用对应的进程号

    采样时长

    60秒

    其他参数

    默认

  8. 查看采集分析结果。
    图4 锁与等待分析结果总览
    图5 锁快照信息

    在“锁实例分析”页签,发现函数“__pthread_mutex_lock”被两个线程调用次数很多;在“锁快照”页签点,查看对应的锁的持有等待时间。

锁抢占优化

实际功能代码的执行速度较快,多线程使用锁的方案会将大量的资源开销花费在锁竞争上,为减少公共资源的的抢占,可通过原子变量无锁的编程方式优化代码。

  1. 程序准备。
    1. 编译pthread_atomic.c并赋予执行文件所有用户可读、可写和可执行权限。
      gcc -g pthread_atomic.c -o pthread_atomic -lpthread && chmod 777 pthread_atomic
      • demo要求GCC版本在7.3以上,若需要关联源码,建议编译时加上-g参数,如:gcc -g pthread_atomic.c -o pthread_atomic -lpthread。
      • 若使用的GCC版本为10.x.x及以上,因该版本默认启用LSE指令集,可能优化效果不明显,建议编译程序时添加参数“-march=armv8-a+nolse -mno-outline-atomics”用以禁用该指令集。
    2. 后台运行程序,nohup命令使得即使退出账户之后会继续运行相应的进程,防止任务中断。
      nohup taskset -c 0-1 ./pthread_atomic >>pthread_atomic.out 2>&1 &

      程序运行的输出(标准输出(1))将会保存到pthread_atomic.out文件,错误信息(2)会重定向到pthread_atomic.out文件。

      该程序一般运行时间为20秒左右,若已运行完成,启动采集任务将无法采集到该程序数据,可修改pthread_atomic.c源码中count参数增加对应运行时间或运行该程序后立即启动采集任务。

  2. 再次创建锁与等待分析任务(pthread_atomic.c应用)。
    表4 任务配置参数说明

    参数

    说明

    分析对象

    应用

    模式

    Attach to process

    分析类型

    锁与等待分析

    PID

    选择pthread_atomic应用对应的进程号

    采样时长

    60秒

    其他参数

    默认

    图6 优化后锁与等待任务

    发现锁实例的计数和总时长大幅降低。

  3. 再次创建资源调度分析任务。
    表5 任务参数配置说明

    参数

    说明

    分析对象

    应用

    模式

    选择“Launch application”

    分析类型

    资源调度分析

    应用路径

    输入示例应用路径,如:/opt/testdemo/pthread_atomic。

    采样时长

    30秒

    其他参数

    默认

    图7 CPU/进程统计

    发现优化后的应用不存在频繁交替抢占,等待时间已被优化。