Linux 如何计算进程id(包括所有未来子线程)的已执行指令数
几天前,我问了下面的问题,@M-Iduoad好心地提供了一个带有Linux 如何计算进程id(包括所有未来子线程)的已执行指令数,linux,performance,profiling,performance-testing,perf,Linux,Performance,Profiling,Performance Testing,Perf,几天前,我问了下面的问题,@M-Iduoad好心地提供了一个带有pgrep的解决方案来捕获所有子PID,并在perf stat中与-p一起使用。它工作得很好 然而,我遇到的一个问题是多线程应用程序,以及在生成新线程时。由于我不是算命师(太糟糕了!),我不知道新生成的线程的tid,因此我无法将它们添加到perf stat的s-p或-t参数中 例如,假设我有一个多线程nodejs服务器(部署为Kubernetes之上的容器),具有以下pstree: root@node2:/home/m# pstre
pgrep
的解决方案来捕获所有子PID,并在perf stat中与-p一起使用。它工作得很好
然而,我遇到的一个问题是多线程应用程序,以及在生成新线程时。由于我不是算命师(太糟糕了!),我不知道新生成的线程的tid
,因此我无法将它们添加到perf stat
的s-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
启动时已经存在的一组固定线程,而不会遵循新线程
有一个类似的例子,但我使用的是
perf stat
。而且,这似乎并没有按线程分隔记录的配置文件,因此它只相当于perf stat node…
,除非有一种方法可以处理记录的数据,以便在事后按线程将其分隔开
perf
如果还有其他可行的方法,则不需要:
无论使用
perf
还是其他任何方法,任何其他可能的解决方案都可以帮助我动态计算给定PID的每个线程(包括新生成的线程)的“指令、周期、任务时钟、cpu时钟、cpu迁移、上下文切换、缓存未命中数” 性能记录-s和性能报告-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 report -T
[... snip ...]
# PID TID instructions:u
270682 270683 500003888
270682 270684 2000001866
270682 270685 4000002177
perf record
的参数有点棘手-s
使用相当精确的数字写入单独的记录-它们不依赖于指令样本(每100000000条指令生成一次)。但是,perf report
,即使使用-T
也会在找不到单个样本时失败。因此,您需要设置至少触发一次的指令样本计数-c
(或频率)。任何样本都可以,每个线程不需要样本
或者,您可以查看perf.data
中的原始记录。然后您可以告诉perf record
不要采集任何样本
但是你需要过滤掉相关的记录,可能还有其他的记录需要总结
$ 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
perf stat
不带-p
跟踪所有线程。e、 g.perf stat节点foo.js
,与链接的perf记录中的问题相同。您的问题是让perf在使用-p
而不是作为perf
@PeterCordes的子线程运行命令时执行什么,我尝试了不使用-p和使用--per线程,但它仍然不会报告perf启动后新分叉/衍生的线程:-(…换句话说,inherit模式似乎关闭了perf stat ffmpeg-i foo.mkv bar。mkv
适合我。我看到190958.96毫秒任务时钟(在我的4c8t Skylake系统上使用了7.027 CPU),占用了27.17秒的挂钟时间。(按q
提前停止后。)对于HW事件,其他计数反映了FFmpeg、libx264和音频编码启动的所有线程的计数。好吧,我很担心;您链接的性能记录实际上并不等同于您想要的性能记录。因此,仅用于perf record
并不是这与您的问题之间的唯一区别。您是否尝试-s
取消性能记录以包括每个线程的统计信息,然后使用性能报告-T
读取性能数据。您将在底部得到一个包含线程及其统计信息的表。每个线程的硬件计数器是否分别虚拟化?例如,是否可能有一个线程生成9999内存指令失效。拆分加载de>事件,但另一个线程实际触发事件并在该线程中仅使用1个未对齐负载获取样本?(在大多数情况下,这不应该是一个实际问题,特别是对于像指令
这样的非常频繁的事件,我只是好奇它是如何在后台工作的;无论是每个线程的样本记录,还是在同一进程的线程之间的上下文切换上保存/恢复的硬件计数器。)我相信整个事件状态与任务紧密相连,并在上下文切换期间进行切换(请参阅)。您也可以长时间地对事件进行计数,甚至不产生溢出中断,Linux应该确保您只从所选的pid获取计数。@Zulan是否也使用了-p?我的意思是提供正在运行的进程的pid?@Zulanperf report-T
似乎不使用-p!但是,梳理您的第二个第一个解决方案给了我最好的结果:perf-record-s-e-c 100000000-p
…解析:perf-script-D | grep perf_-record|u READ | grep-v“0$”| grep
是的,我认为-p
不可能遵循新生成的子进程。我假设您可以影响
$ 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