使用 Linux Perf 进行分析

Linux 的 perf 工具可以对应用程序可能产生的所有线程进行性能分析。它有 -s 选项,可以记录每个线程的事件计数。使用此选项,在报告的末尾,perf 列出了所有线程 ID 以及每个线程收集的样本数:

$ perf record -s ./x264 -o /dev/null --slow --threads 8 Bosphorus_1920x1080_120fps_420_8bit_YUV.y4m
$ perf report -n -T
...
#  PID   TID   cycles:ppp
  6966  6976  41570283106
  6966  6971  25991235047
  6966  6969  20251062678
  6966  6975  17598710694
  6966  6970  27688808973
  6966  6972  23739208014
  6966  6973  20901059568
  6966  6968  18508542853
  6966  6967     48399587
  6966  6966   2464885318

要为特定的软件线程过滤样本,可以使用 --tid 选项:

$ perf report -T --tid 6976 -n
# Overhead  Samples  Shared Object  Symbol
# ........  .......  .............  ...................................
     7.17%   19877       x264       get_ref_avx2
     7.06%   19078       x264       x264_8_me_search_ref
     6.34%   18367       x264       refine_subpel
     5.34%   15690       x264       x264_8_pixel_satd_8x8_internal_avx2
     4.55%   11703       x264       x264_8_pixel_avg2_w16_sse2
     3.83%   11646       x264       x264_8_pixel_avg2_w8_mmx2

Linux 的 perf 也自动提供了我们在 [@sec:secMT_metrics] 中讨论的一些指标:

$ perf stat ./x264 -o /dev/null --slow --threads 8 Bosphorus_1920x1080_120fps_420_8bit_YUV.y4m
         86,720.71 msec task-clock        #    5.701 CPUs utilized
            28,386      context-switches  #    0.327 K/sec
             7,375      cpu-migrations    #    0.085 K/sec
            38,174      page-faults       #    0.440 K/sec
   299,884,445,581      cycles            #    3.458 GHz
   436,045,473,289      instructions      #    1.45  insn per cycle
    32,281,697,229      branches          #  372.249 M/sec
       971,433,345      branch-misses     #    3.01% of all branches

查找开销大的锁

要使用 Linux 的 perf 找到最有争议的同步对象,需要对调度程序上下文切换进行采样(sched:sched_switch),这是一个内核事件,因此需要 root 访问权限:

$ sudo perf record -s -e sched:sched_switch -g --call-graph dwarf -- ./x264 -o /dev/null --slow --threads 8 Bosphorus_1920x1080_120fps_420_8bit_YUV.y4m
$ sudo perf report -n --stdio -T --sort=overhead,prev_comm,prev_pid --no-call-graph -F overhead,sample
# Samples: 27K of event 'sched:sched_switch'
# Event count (approx.): 27327
# Overhead       Samples         prev_comm    prev_pid
# ........  ............  ................  ..........
    15.43%          4217              x264        2973
    14.71%          4019              x264        2972
    13.35%          3647              x264        2976
    11.37%          3107              x264        2975
    10.67%          2916              x264        2970
    10.41%          2844              x264        2971
     9.69%          2649              x264        2974
     6.87%          1876              x264        2969
     4.10%          1120              x264        2967
     2.66%           727              x264        2968
     0.75%           205              x264        2977

上面的输出显示了哪些线程最频繁地被切换出执行。请注意,我们还收集了调用堆栈(--call-graph dwarf,见 [@sec:secCollectCallStacks]),因为我们需要用它来分析导致昂贵同步事件的路径:

$ sudo perf report -n --stdio -T --sort=overhead,symbol -F overhead,sample -G
# Overhead       Samples  Symbol
# ........  ............  ...........................................
   100.00%         27327  [k] __sched_text_start                     
   |
   |--95.25%--0xffffffffffffffff
   |  |
   |  |--86.23%--x264_8_macroblock_analyse
   |  |  |
   |  |   --84.50%--mb_analyse_init (inlined)
   |  |     |
   |  |      --84.39%--x264_8_frame_cond_wait
   |  |        |
   |  |         --84.11%--__pthread_cond_wait (inlined)
   |  |                   __pthread_cond_wait_common (inlined)
   |  |                   |
   |  |                    --83.88%--futex_wait_cancelable (inlined)
   |  |                              entry_SYSCALL_64
   |  |                              do_syscall_64
   |  |                              __x64_sys_futex
   |  |                              do_futex
   |  |                              futex_wait
   |  |                              futex_wait_queue_me
   |  |                              schedule
   |  |                             

 __sched_text_start
   ...

上面的列表显示了导致等待条件变量 (__pthread_cond_wait) 和后续上下文切换的最频繁路径。这条路径是 x264_8_macroblock_analyse -> mb_analyse_init -> x264_8_frame_cond_wait。从这个输出中,我们可以得知 84% 的上下文切换都是由线程在 x264_8_frame_cond_wait 中等待条件变量引起的。

results matching ""

    No results matching ""