【问题标题】:How to count number of executed instructions of a process id including all future child threads如何计算进程ID的执行指令数,包括所有未来的子线程
【发布时间】:2021-01-12 12:29:03
【问题描述】:

前段时间,我问了以下问题"How to count number of executed instructions of a process id including child processes",@M-Iduoad 提供了一个解决方案,pgrep 捕获所有子 PID,并在 perf stat 中与 -p 一起使用。效果很好!

但是,我遇到的一个问题是多线程应用程序,以及何时产生新线程。由于我不是算命先生(太糟糕了!),我不知道tid 新生成的线程,因此我无法将它们添加到perf stat 的-p 或-t 参数中。

作为示例,假设我有一个多线程 nodejs 服务器(部署为 Kubernetes 之上的容器),带有以下 pstree

root@node2:/home/m# pstree -p 4037791
node(4037791)─┬─sh(4037824)───node(4037825)─┬─{node}(4037826)
              │                             ├─{node}(4037827)
              │                             ├─{node}(4037828)
              │                             ├─{node}(4037829)
              │                             ├─{node}(4037830)
              │                             └─{node}(4037831)
              ├─{node}(4037805)
              ├─{node}(4037806)
              ├─{node}(4037807)
              ├─{node}(4037808)
              ├─{node}(4037809)
              ├─{node}(4037810)
              ├─{node}(4037811)
              ├─{node}(4037812)
              ├─{node}(4037813)
              └─{node}(4037814) 

当然,我可以使用下面的perf stat 命令来查看它的线程:

perf stat --per-thread -e instructions,cycles,task-clock,cpu-clock,cpu-migrations,context-switches,cache-misses,duration_time -p $(pgrep --ns 4037791 | paste -s -d ",")

它适用于单线程 nodejs 应用程序。但在多线程服务的情况下,一旦收到请求,pstree 的输出将如下所示:

root@node2:/home/m# pstree -p 4037791
node(4037791)─┬─sh(4037824)───node(4037825)─┬─{node}(4037826)
              │                             ├─{node}(4037827)
              │                             ├─{node}(4037828)
              │                             ├─{node}(4037829)
              │                             ├─{node}(4037830)
              │                             ├─{node}(4037831)
              │                             ├─{node}(1047898)
              │                             ├─{node}(1047899)
              │                             ├─{node}(1047900)
              │                             ├─{node}(1047901)
              │                             ├─{node}(1047902)
              │                             ├─{node}(1047903)
              │                             ├─{node}(1047904)
              │                             ├─{node}(1047905)
              │                             ├─{node}(1047906)
              │                             ├─{node}(1047907)
              │                             ├─{node}(1047908)
              │                             ├─{node}(1047909)
              │                             ├─{node}(1047910)
              │                             ├─{node}(1047911)
              │                             ├─{node}(1047913)
              │                             ├─{node}(1047914)
              │                             ├─{node}(1047919)
              │                             ├─{node}(1047920)
              │                             ├─{node}(1047921)
              │                             └─{node}(1047922)
              ├─{node}(4037805)
              ├─{node}(4037806)
              ├─{node}(4037807)
              ├─{node}(4037808)
              ├─{node}(4037809)
              ├─{node}(4037810)
              ├─{node}(4037811)
              ├─{node}(4037812)
              ├─{node}(4037813)
              └─{node}(4037814)

因此,我之前的perf stat 命令不会捕获新生成线程的统计信息。我的意思是,它可能会捕获累积的指令,但绝对不会以“每线程”格式显示。

有什么方法可以在 perf stat 中使用--per-thread 并捕获多线程应用程序中新产生的线程的统计信息?它似乎只与-p-t 一起工作,以跟随perf 启动时已经存在的一组固定线程,并且不会跟随新线程。


有一个类似的question here for perf record,但我使用的是perf stat。另外,这似乎并没有将记录的配置文件按线程分开,所以它仅相当于perf stat node ... 除非有办法处理记录的数据以在事后按线程将其分开?


perf 不是必需的,如果还有其他方法可以使用:

任何其他可以帮助我动态计算给定 PID 的每个线程(包括新生成的线程)的“指令、周期、任务时钟、cpu 时钟、cpu 迁移、上下文切换、缓存未命中”的潜在解决方案是可以接受,无论是使用perf 还是其他任何东西!

【问题讨论】:

  • perf stat 没有 -p 会跟踪所有线程。例如perf stat node foo.js,与链接的perf record 问题相同。当您使用 -p 而不是将命令作为 perf 的子级运行时,您的问题是要执行什么操作。
  • @PeterCordes 我在没有 -p 和 --per-thread 的情况下尝试过,但是在 perf start 之后它仍然没有报告新的分叉/派生线程 :-( ...换句话说,继承模式似乎离开
  • perf stat ffmpeg -i foo.mkv bar.mkv 为我工作。我看到了190,958.96 msec task-clock # 7.027 CPUs utilized(在我的 4c8t Skylake 系统上),挂钟时间经过了 27.17 秒。 (在按下q 提前停止之后。)对于硬件事件,其他计数反映了由 FFmpeg、libx264 和音频编码启动的所有线程的计数。
  • 好吧,我害怕那个; can "perf record" or "perf-record" sample child processes? 您链接的 不是 实际上是您想要的性能记录等价物。因此,仅仅为perf record 服务并不是这与您的问题之间的唯一区别。
  • 您是否尝试过 perf record-s 包含每个线程的统计信息,然后使用 perf report -T 读取 perf.data 您将在底部获得一个包含线程及其统计信息的表格.

标签: linux performance profiling performance-testing perf


【解决方案1】:

perf record -sperf report -T 的组合应该为您提供所需的信息。

为了演示,请使用以下示例代码,使用具有明确定义的指令计数的线程:

#include <cstdint>
#include <thread>

void work(int64_t count) {
    for (int64_t i = 0; i < count; i++);
}

int main() {
    std::thread first(work, 100000000ll);
    std::thread second(work, 400000000ll);
    std::thread third(work, 800000000ll);
    first.join();
    second.join();
    third.join();
}

(编译不优化!)

现在,使用perf record 作为前缀命令。它将跟随所有衍生的进程和线程。

$ perf record -s -e instructions -c 1000000000 ./a.out
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.003 MB perf.data (5 samples) ]

为了更好地显示统计数据:

$ perf report -T
[... snip ...]
#    PID     TID  instructions:u
  270682  270683       500003888
  270682  270684      2000001866
  270682  270685      4000002177

perf record 的参数有点棘手。 -s 用相当精确的数字写入单独的记录 - 它们不依赖于指令样本(每 1000000000 条指令生成)。但是,perf report,即使使用-T,在找不到单个样本时也会失败。所以你需要设置一个至少触发一次的指令样本计数-c(或频率)。任何样本都可以,不需要每个线程一个样本。

或者,您可以查看来自perf.data 的原始记录。然后你实际上可以告诉perf record 不要收集任何样本。

$ perf record -s -e instructions -n ./a.out             
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.003 MB perf.data ]

但是你需要过滤掉相关记录,可能还有额外的记录你需要总结。

$ perf script -D | grep PERF_RECORD_READ | grep -v " 0$"
# Annotation by me                              PID    TID 
213962455637481 0x760 [0x40]: PERF_RECORD_READ: 270887 270888 instructions:u 500003881
213963194850657 0x890 [0x40]: PERF_RECORD_READ: 270887 270889 instructions:u 2000001874
213964190418415 0x9c0 [0x40]: PERF_RECORD_READ: 270887 270890 instructions:u 4000002175

【讨论】:

  • 硬件计数器是否为每个线程单独虚拟化?例如一个线程是否有可能生成 9999 mem_inst_retired.split_loads 事件,但另一个线程实际触发事件并在该线程中仅获得 1 个未对齐负载的样本? (在大多数情况下不应该是一个实际问题,特别是对于像instructions 这样的非常频繁的事件,我只是好奇它是如何在引擎盖下工作的;无论是样本记录是每个线程还是硬件计数器在同一进程的线程之间进行上下文切换时保存/恢复。)
  • 我相信整个事件状态与任务紧密绑定并在上下文切换期间切换(参见perf_event_context_sched_out)。您也可以长时间计算事件而不产生溢出中断,Linux 应确保您仅从选定的 pid 获取计数。
  • @Zulan 它也可以与 -p 一起使用吗?我的意思是提供正在运行的进程的 PID?
  • @Zulan perf report -T 似乎不适用于-p!但是,将您的第二个解决方案与第一个解决方案结合起来给了我最好的结果:perf record -s -e &lt;my events&gt; -c 1000000000 -p &lt;myprocesses&gt; ... 解析:perf script -D | grep PERF_RECORD_READ | grep -v " 0$" | grep &lt;event name&gt;
  • 是的,我认为-p 不可能跟踪新生成的子进程。我假设您可以影响应用程序的初始生成。
猜你喜欢
  • 2020-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-17
  • 1970-01-01
  • 2023-03-09
  • 2014-01-06
相关资源
最近更新 更多