• 如何使用perf top 探究性能


    Perf 内置于Linux 内核源码树中的性能剖析工具。它基于事件采样原理,以性能事件为基础,支持针对处理器相关性能指标与操作系统相关性能指标的性能剖析。可用于性能瓶颈的查找与热点代码的定位。linux2.6及后续版本都自带该工具,几乎能够处理所有与性能相关的事件。perf top 是比较常用于展示占用CPU始终最多的函数或者指令,一般以此来查找热点函数。

     

    实战

    root@xxxx:~# apt install linux-tools-common
    root@xxxx:~# perf top
    
    Samples: 6K of event 'cycles', 4000 Hz, Event count (approx.): 48144737 lost: 0/0 drop: 0/0
    Overhead  Shared Object                           Symbol
       3.84%  [kernel]                                [k] native_write_msr
       2.25%  [kernel]                                [k] update_blocked_averages
       1.89%  [kernel]                                [k] update_sd_lb_stats.constprop.0
       1.80%  [kernel]                                [k] pvclock_clocksource_read
    

    Samples : 采样数, perf 总共采集了 6K 个 CPU 时钟事件。

    event : 事件类型。

    Event count (approx.) : 事件总数量

     

    行列:

    Overhead: 是该符号的性能事件在所有采样中的比例,用百分比来表示。

    Shared: Shared ,是该函数或指令所在的动态共享对象(Dynamic Shared Object),如内核、进程名、动态链接库名、内核模块名等。

    Object: Object ,是动态共享对象的类型。比如 [.] 表示用户空间的可执行程序、或者动态链接库,而 [k] 则表示内核空间。

    Symbol: Symbol 是符号名,也就是函数名。当函数名未知时,用十六进制的地址来表示。

    上面结果显示内核模块中native_write_msr 占用 CPU时钟最多,比列占3.84%。

    同样可以使用perf record 与 perf report 进行定时采集再展示。加上 -g 参数,开启调用关系的采样,方便我们根据调用链来分析性能问题。

    root@xxxx:~# perf record
    ^C //(结束采集)
    [ perf record: Woken up 1 times to write data ]
    [ perf record: Captured and wrote 0.967 MB perf.data (739 samples) ]
    
    root@xxxx:~# perf report
    
    Samples: 739  of event 'cycles', Event count (approx.): 22853303
    Overhead  Command          Shared Object             Symbol
       4.38%  swapper          [kernel.kallsyms]         [k] native_write_msr
       4.04%  swapper          [kernel.kallsyms]         [k] native_safe_halt
       3.26%  swapper          [kernel.kallsyms]         [k] update_blocked_averages
       2.63%  swapper          [kernel.kallsyms]         [k] pvclock_clocksource_read
       2.07%  swapper          [kernel.kallsyms]         [k] update_sd_lb_stats.constprop.0
       1.94%  swapper          [kernel.kallsyms]         [k] rcu_cblist_dequeue
       1.79%  swapper          [kernel.kallsyms]         [k] _raw_spin_lock_irqsave
       1.76%  swapper          [kernel.kallsyms]         [k] ktime_get
    

    利用 Nginx PHP做演示

    root@xxxx:~# docker run --name nginx -p 10000:80 -itd feisky/nginx
    root@xxxx:~# docker run --privileged --name phpfpm -itd --network container:nginx feisky/php-fpm
    
    root@xxxx:~# curl http://localhost:10000/
    It works!
    

    接着,我们来测试一下这个 Nginx 服务的性能。在第二个终端运行下面的 ab 命令:

    root@xxxx:~# ab -c 10 -n 100 http://localhost:10000/
    This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
    Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
    Licensed to The Apache Software Foundation, http://www.apache.org/
    
    Benchmarking localhost (be patient).....done
    
    
    Server Software:        nginx/1.15.4
    Server Hostname:        localhost
    Server Port:            10000
    
    Document Path:          /
    Document Length:        9 bytes
    
    Concurrency Level:      10
    Time taken for tests:   1.188 seconds
    Complete requests:      100
    Failed requests:        0
    Total transferred:      17200 bytes
    HTML transferred:       900 bytes
    Requests per second:    84.14 [#/sec] (mean)
    Time per request:       118.849 [ms] (mean)
    
    Time per request:       11.885 [ms] (mean, across all concurrent requests)
    Transfer rate:          14.13 [Kbytes/sec] received
    

    从 ab 的输出结果我们可以看到,Nginx 能承受的每秒平均请求数只有 84.14。

    继续在第二个终端,运行 ab 命令:

    root@xxxx:~# ab -c 10 -n 10000 http://localhost:10000/
    

    接着,回到第一个终端使用docker exec 指令进入容器,然后运行 top 命令,并按下数字 1 ,切换到每个 CPU 的使用率:

    top - 17:50:45 up 21:25,  3 users,  load average: 1.11, 0.27, 0.12
    Tasks: 243 total,   6 running, 237 sleeping,   0 stopped,   0 zombie
    %Cpu0  : 98.0 us,  1.7 sy,  0.0 ni,  0.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    %Cpu1  : 98.7 us,  1.3 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    %Cpu2  : 99.0 us,  0.7 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.3 si,  0.0 st
    %Cpu3  : 99.0 us,  1.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    MiB Mem :   7961.2 total,   4756.2 free,    993.1 used,   2211.9 buff/cache
    MiB Swap:   2048.0 total,   2048.0 free,      0.0 used.   6669.1 avail Mem
    
    
    // 在容器内部top
    KiB Swap:  2097148 total,  2097148 free,        0 used.  6706484 avail Mem
     Unknown command - try 'h' for help
        PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
        552 daemon    20   0  336700  15420   7744 R  93.3  0.2   0:04.42 php-fpm
        553 daemon    20   0  336700  15420   7744 R  86.7  0.2   0:04.14 php-fpm
        555 daemon    20   0  336700  15420   7744 R  86.7  0.2   0:03.55 php-fpm
        551 daemon    20   0  336700  15420   7744 R  53.3  0.2   0:03.78 php-fpm
        554 daemon    20   0  336700  15420   7744 R  46.7  0.2   0:04.51 php-fpm
          1 root      20   0  336316  47672  40276 S   0.0  0.6   0:00.31 php-fpm
          7 root      20   0   18196   3416   2872 S   0.0  0.0   0:00.00 bash
        556 root      20   0   43152   3260   2768 R   0.0  0.0   0:00.00 top
    

     

    这里可以看到,系统中有几个 php-fpm 进程的 CPU 使用率加起来接近 400%;而每个 CPU 的用户使用率(us)也已经超过了 98%,接近饱和。这样,我们就可以确认,正是用户空间的 php-fpm 进程,导致 CPU 使用率骤升。那再往下走,怎么知道是 php-fpm 的哪个函数导致了 CPU 使用率升高呢?我们来用 perf 分析一下。

    root@xxxx:~# docker exec -i -t phpfpm bash // 进入容器
    
    //容器内部
    root@xxxx:~# apt-get update && apt-get install -y linux-perf linux-tools procps
    root@xxxx:~# perf top -g -p 555
    

    按方向键切换到 php-fpm,再按下回车键展开 php-fpm 的调用关系,你会发现,调用关系最终到了 sqrt 和 add_function。看来,我们需要从这两个函数入手了。

    我们拷贝出 Nginx 应用的源码,看看是不是调用了这两个函数:

    //从容器phpfpm中将PHP源码拷贝出来
    root@xxxx:~# docker cp phpfpm:/app .
    
    //使用grep查找函数调用
    root@xxxx:~# grep sqrt -r app/
    app/index.php:  $x += sqrt($x);
    
    root@xxxxx:~# cat app/index.php
    <?php
    // test only.
    $x = 0.0001;
    for ($i = 0; $i <= 1000000; $i++) {
      $x += sqrt($x);
    }
    
    echo "It works!"
    ?>root
    

    可以看得出这段测试代码导致运行速度变慢。

    # 停止原来的应用
    root@xxxx:~# docker rm -f nginx phpfpm
    # 运行优化后的应用
    root@xxxx:~# docker run --name nginx -p 10000:80 -itd feisky/nginx:cpu-fix
    root@xxxx:~# docker run --name phpfpm -itd --network container:nginx feisky/php-fpm:cpu-fix
    

    删除测试代码后再次编译运行。

    $ ab -c 10 -n 10000 http://localhost:10000/
    ...
    Complete requests:      10000
    Failed requests:        0
    Non-2xx responses:      10000
    Total transferred:      3030000 bytes
    HTML transferred:       1530000 bytes
    Requests per second:    12273.25 [#/sec] (mean)
    Time per request:       0.815 [ms] (mean)
    Time per request:       0.081 [ms] (mean, across all concurrent requests)
    Transfer rate:          3631.64 [Kbytes/sec] received
    
    ...
    

    从这里你可以发现,现在每秒的平均请求数,已经从原来的 84.14 变成了 12273.25

    1.用户 CPU 和 Nice CPU 高,说明用户态进程占用了较多的 CPU,所以应该着重排查进程的性能问题。

    2.系统 CPU 高,说明内核态占用了较多的 CPU,所以应该着重排查内核线程或者系统调用的性能问题。

    3.I/O 等待 CPU 高,说明等待 I/O 的时间比较长,所以应该着重排查系统存储是不是出现了 I/O 问题。

    4.软中断和硬中断高,说明软中断或硬中断的处理程序占用了较多的 CPU,所以应该着重排查内核中的中断服务程序。

     

    结束语

    碰到 CPU 使用率升高的问题,你可以借助 top、pidstat 等工具,确认引发 CPU 性能问题的来源;再使用 perf 等工具,排查出引起性能问题的具体函数。

  • 相关阅读:
    vue报错 error: data.push is not a function
    vue elment.style样式修改(第三方组件自生成元素)
    按元素标签查询多个
    按css查询多个元素
    按CSS查询一个元素
    查询单个元素
    JavaScript 查找元素
    Spring 商品分类
    Spring 使用日志
    Spring 使用日期类型
  • 原文地址:https://www.cnblogs.com/eflypro/p/14848449.html
Copyright © 2020-2023  润新知