gperftools 使用说明

**

gperftools 使用说明

**

1.实现原理

gperftools 基于采样技术来分析应用的性能;这种技术的基本思想是在程序运行过程中以固定的时间间隔,记录当前的调用栈。通过收集大量的样本,我们就可以得到程序在各个函数中花费的时间的大致比例。

  • 调用栈的信息:调用栈可以告诉我们程序在某一时刻正在执行哪个函数,以及这个函数是由哪个函数调用的。这样我们就可以知道程序的执行流程。
  • 统计信息:通过在多个时间点收集调用栈信息,我们可以统计每个函数被调用的次数,将每个函数出现在栈顶的次数除以总的样本数,得到这个函数占用cpu时间的比例;这个比例被认为函数平均执行时间的估计。
  • 性能瓶颈:如果一个函数在调用栈中出现的次数特别多,那么这个函数可能就是性能瓶颈。我们可以对这个函数进行优化,以提高程序的整体性能。

2.使用方法-采样

在应用链接libprofiler.so 后(代码里要调用以下profiler里的一个接口,否则libprofiler 会被忽略), 默认不会启动profile 采样,可以通过以下方法控制启停:
2.1 使用API控制
在应用代码里主动的调用start /stop 函数:
/* Start profiling and write profile info into fname, discarding any

  • existing profiling data in that file.
  • This is equivalent to calling ProfilerStartWithOptions(fname, NULL).
    /
    PERFTOOLS_DLL_DECL int ProfilerStart(const char
    fname);

/* Stop profiling. Can be started again with ProfilerStart(), but

  • the currently accumulated profiling data will be cleared.
    */
    PERFTOOLS_DLL_DECL void ProfilerStop(void);
    2.2 使用环境变量控制
    export CPUPROFILE=“/work/qnx/cpuprof”
    export CPUPROFILESIGNAL=“12”

#运行
./build/cpuprof
#启动profile
kill -n 12 $(pidof cpuprof)
#停止profile
kill -n 12 $(pidof cpuprof)

2.3 对线程profile
对线程profile 需要同时执行以下操作:

  1. 设置环境变量export CPUPROFILE_PER_THREAD_TIMERS=“1”

  2. 确保timer_create函数在程序里被调用过

  3. 在目标线程的入口调用ProfileHandlerRegisterThread()函数
    2.4 注意事项

  4. Profiler 默认使用的是普通定时器;若是有线程屏蔽了定时器信号,这个线程就不能被采样

  5. start/stop之间的间隔越长,收集到的数据越多, 分析结果越准确

3.profile分析

对xx采样,获取数据 cpuprof.0-0619。 可以通过以下两种方式,查看主要耗时函数
3.1 命令行方式
命令方式简单直观,能够快速看到热点函数:
root@a1000:/userdata# ./pprof ./xx/opt/xx/bin/xx /work/qnx/cpuprof.0-0619
File: xx
Type: cpu
Entering interactive mode (type “help” for commands, “o” for options)
(pprof) top
Showing nodes accounting for 124.15s, 34.39% of 361.04s total
Dropped 2434 nodes (cum <= 1.81s)
Showing top 10 nodes out of 206
flat flat% sum% cum cum%
49.54s 13.72% 13.72% 49.54s 13.72% _IO_str_seekoff
15.50s 4.29% 18.01% 15.50s 4.29% nanosleep
15.39s 4.26% 22.28% 48.92s 13.55% malloc
8.54s 2.37% 24.64% 8.54s 2.37% google::protobuf::io::CodedOutputStream::WriteVarint32ToArray
7.48s 2.07% 26.71% 8.04s 2.23% pthread_cond_timedwait
6.62s 1.83% 28.55% 68.76s 19.04% google::protobuf::Arena::CreateMaybeMessage
5.52s 1.53% 30.08% 11.32s 3.14% apserial::ref_float32_t::_InternalSerialize
5.46s 1.51% 31.59% 14.34s 3.97% apserial::ref_float32_t::ByteSizeLong
5.24s 1.45% 33.04% 5.24s 1.45% free
4.86s 1.35% 34.39% 4.86s 1.35% google::protobuf::internal::InternalMetadataWithArenaBase::~InternalMetadataWithArenaBase

3.2 web页面
Web 页面可以提供非常丰富的信息,依据调用链,它可以直观的展示热点函数的主要执行路径。

  1. 构造二进制目录

  2. 将执行程序和相关依赖库按照板端的路径打包

  3. 将二进制包放到WSL或者其他Linux虚拟机里

  4. 执行分析指令
    root@J2P14s0187:/mnt/d/debuglogs/cpuprof# export PPROF_BINARY_PATH=/mnt/d/debuglogs/cpuprof/xx
    root@J2P14s0187:/mnt/d/debuglogs/cpuprof# ./pprof -http 0.0.0.0:8001 -no_browser ./xx/userdata/xx/opt/APMonitor/bin/xx. /apmonitor/cpuprof.0-0619
    Serving web UI on http://0.0.0.0:8001

  5. 从浏览器查看
    在出现Serving web UI on http://0.0.0.0:8001 后, 从浏览器登陆8001端口查看:
    在这里插入图片描述
    在这里插入图片描述

4.实现分析

4.1 定时器
4.1.1 线程定时器和普通定时器
定时器分为线程定时器和普通定时器两种,主要区别在于它们发送信号的方式和目标。

  • 线程定时器:当线程定时器到期时,它会向创建它的线程发送信号。这意味着,即使在多线程环境中,也可以确保信号被正确地发送到了预期的线程。这在需要对特定线程进行精确计时或调度的情况下非常有用。
  • 普通定时器:当普通定时器到期时,它会向进程发送信号。在多线程环境中,这可能会导致信号被任何一个线程接收,这取决于操作系统的调度策略。因此,普通定时器更适合于对整个进程进行计时或调度。
    默认情况下,ProfileHandle 使用普通定时器,只有同时满足以下条件才会使用线程定时器:
  • gperftools编译时开启线程定时器支持(默认开启)
  • 指定了环境变量“CPUPROFILE_PER_THREAD_TIMERS”或“CPUPROFILE_TIMER_SIGNAL”
  • 若链接技术找到了timer_create的实现

4.1.2 定时器类型
setitimer 是一个在 Unix-like 系统(如 Linux)中用于设置一个间隔定时器的系统调用。当定时器到期时,系统将向进程发送一个信号。
setitimer 函数的原型如下:
#include <sys/time.h>

int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);

这个函数接收三个参数:

  1. which:这个参数指定了定时器的类型,可以是以下三种之一:
    - ITIMER_REAL:当此类型的定时器到期时,系统将向进程发送 SIGALRM 信号。这个定时器以实际的时间(或者说“墙钟时间”)来计算。
    - ITIMER_VIRTUAL:当此类型的定时器到期时,系统将向进程发送 SIGVTALRM 信号。这个定时器只在进程在用户态执行时才计时。
    - ITIMER_PROF:当此类型的定时器到期时,系统将向进程发送 SIGPROF 信号。这个定时器在进程在用户态执行和系统态执行时都会计时。

  2. new_value:这是一个指向 itimerval 结构体的指针,该结构体指定了定时器的新值。itimerval 结构体定义如下:
    struct itimerval {
    struct timeval it_interval; /* next value /
    struct timeval it_value; /
    current value */
    };

其中,it_interval 是定时器到期后的新值,it_value 是定时器的当前值。如果 it_value 不为零,那么定时器将在 it_value 指定的时间后到期;如果 it_value 为零,那么定时器将停止。如果 it_interval 不为零,那么定时器将在每次到期后自动重置为 it_interval 指定的值。
3. old_value:这是一个指向 itimerval 结构体的指针,如果不为 NULL,那么系统将在这里存储定时器的旧值。
3.2 采样
注册定时器的信号处理函数,在定时器处理函数里,记录线程的调用栈。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值