在Linux上进行线程利用率分析

17

Linux性能工具非常适合找到CPU周期热点并优化这些热点。但一旦某些部分被并行化,就很难发现顺序执行的部分,因为它们占据了相当大的墙上时间,但不一定需要很多CPU周期(并行部分已经在消耗这些周期)。

为避免XY问题:我的基本动机是找到多线程代码中的顺序瓶颈。尽管由于阿姆达尔定律顺序执行的阶段占据了墙上的时间,但并行阶段可以轻松支配聚合CPU周期统计数据。

对于Java应用程序,使用visualvm或yourkit很容易实现线程利用率时间轴。

yourkit thread timeline

请注意,它显示所选时间范围或时间点的线程状态(可运行,等待,阻塞)和堆栈样本。

如何使用perf或其他Linux本机分析器实现类似的功能?它不必是GUI可视化,只要找到顺序瓶颈和与其关联的CPU样本即可。

另请参见更窄后续问题,重点关注perf


屏幕截图带有时间轴,来自追踪工具而非性能分析工具。请查看 KernelShark + Trace-cmd 或 LTTng 追踪器以获取相同的结果。Perf是通用的,即使在默认模式下(通过 perf script 打印),perf.data 中也可能包含一些信息;但要获取有关线程调度的精确信息,还应跟踪sched_*事件。而且,perf 通常只对 CPU 上运行的线程进行性能分析(尤其是当您不在 AWS 或其他虚拟化中,并且可以使用硬件计数器“cycles”时),而不是墙上的时间。 - osgx
@osgx 好的,当线程不在 CPU 上运行时,我可以不需要样本。原则上,我只是想弄清楚它在花费时间(~=获取样本)在单个线程上的时期内做了什么。这可能是一个有点 XY 问题。我的目标是找到单线程瓶颈。对我来说最明显的方法是按线程基础可视化堆栈样本。 - the8472
perf 的默认模式是 on-CPU sampling profiling。尝试交互式 perf report 文本用户界面或选项来“聚焦”于某些线程:perf record -g -F 99 -s ./your_program; perf report -Tperf report -T --tid=$TID,其中 $TID 是一个线程的 pid 或逗号分隔列表。我没有测试 -s/-T 选项来拆分线程统计信息,但它们已经被记录在文档中:http://man7.org/linux/man-pages/man1/perf-record.1.html http://man7.org/linux/man-pages/man1/perf-report.1.html;per-thread 是默认模式:https://perf.wiki.kernel.org/index.php/Tutorial#Collecting_samples - osgx
1
@osgx,除非我漏掉了什么,否则它们并没有帮助,因为它们只提供聚合统计信息。我感兴趣的不是聚合,而是样本子集,在这些子集中只有一个线程处于活动状态。基本上,我对代码已经并发的时间不感兴趣,但是在报告中占主导地位,尽管它们可能不占支配地位。这就是Java中那些线程时间轴视图所提供的内容(它们还显示特定时间片段的线程堆栈-未在截屏中显示-但对于解决问题很重要)。 - the8472
Intel VTune支持这种类型的可视化。但是它只对学生和教育工作者免费。 - Hadi Brais
2个回答

4

Oracle's Developer Studio Performance Analyzer可能正好符合你的要求。(如果你在使用Solaris,我知道它一定可以满足你的需求,但我从未在Linux上使用过它,并且现在也没有适合尝试它的Linux系统可用)。

这是在x86 Solaris 11系统上运行的多线程IO测试程序的屏幕截图:

Screenshot of multithreaded IO performance test prorgam

请注意,您可以查看每个线程的调用堆栈,以及查看线程之间的交互方式-在发布的示例中,您可以看到实际执行IO的线程从哪里开始,您可以看到每个线程在执行时的情况。
这是一个视图,显示了第二个线程在突出显示的时刻所处的位置:

enter image description here

这个视图启用了同步事件视图,显示线程2在高亮时段内被卡在sem_wait调用中。请注意额外的图形数据行,显示同步事件(sem_wait()pthread_cond_wait()pthread_mutex_lock()等):

enter image description here

其他视图包括调用树:

enter image description here

线程概览(如果只有少量线程,则不太有用,但如果有数百个或更多,则可能非常有用。

enter image description here

和一个显示函数CPU利用率的视图

enter image description here

你可以看到每行代码花费的时间:

enter image description here

毫不意外的是,一个正在写入大文件以测试IO性能的进程几乎所有时间都花在了write()函数上。

完整的Oracle简报请参见https://www.oracle.com/technetwork/server-storage/solarisstudio/documentation/o11-151-perf-analyzer-brief-1405338.pdf

快速使用概述:


其他视图似乎没有按时间轴范围限定,或者说它们是吗?如果不是的话,那么它们就不能用于深入到单线程运行的时间范围。 - the8472
@the8472 嗯,你可以在时间轴视图中看到线程之间发生的情况。我添加了一个视图,同步事件可见。你可以看到高亮线程正在sem_wait()等待。不确定你所说的“由时间轴范围限定”。你是否正在寻找基于时间轴选择使用了多少CPU时间的数据? - Andrew Henle
有点类似。我真正想要的是查看运行线程的堆栈样本,其中应用程序并未完全并行化。瓶颈所在。在时间轴上寻找其他线程处于空闲状态时的一个活动线程,然后选择该范围并获取该范围的聚合统计信息,这是我使用熟悉的Java工具进行的方式。也可以使用其他过滤方式,或者更好的方式,因为仅选择一次瓶颈而不是整体统计数据。 - the8472
是的,我在谈论其他截图,它们似乎与问题无关。如果在时间轴上对许多样本进行范围选择,是否会在调用堆栈视图中显示聚合样本或仅显示单个堆栈跟踪?如果只有一个堆栈跟踪,那么它与@GalS建议的链式图形没有任何区别。 - the8472
1
在经历了一些困难后,我终于让它工作了。它需要一个古老的Java版本,并且在使用jemalloc的应用程序上会挂起,我不得不重新编译使用glibc malloc。 - the8472
显示剩余4条评论

3
您可以使用我们用于分析Off-CPU Analysis的强大工具Off-CPU Flame Graphs来获得所需的结果,它是Flame Graphs的一部分。
我使用了Off-CPU analysis
Off-CPU分析是一种性能方法,其中测量和研究离线CPU时间以及堆栈跟踪等上下文。它与CPU分析不同,后者仅检查执行在CPU上的线程。
这个工具基于您提到的首选工具-perf、bcctools,但它提供了一个非常易于使用的输出,称为火焰图,交互式SVG文件看起来像这样SVG Off-CPU Time Flame Graph

enter image description here

宽度与代码路径中总时间成比例,因此首先查找最宽的塔以了解延迟的最大来源。从左到右的排序没有意义,y轴是堆栈深度。

Off-CPU Flame Graphs中的另外两个有用的分析方法也可以帮助你 - 就我个人而言,我没有尝试过。

Wakeup

这让我们能够解决比仅使用off-CPU跟踪更多的问题,因为唤醒信息可以解释阻塞的真正原因。

Chain Graph

链图是一种实验性的可视化,将off-CPU堆栈与它们的wakeup堆栈相关联

还有一种实验性的可视化,结合了CPU和Off-CPU Flame Graphs,Hot/Cold Flame Graphs

这展示了所有线程时间在同一个图表上,能够直接比较在CPU和非CPU代码路径持续的时间。
需要花一点时间了解这个性能分析工具并理解其概念,但使用它非常容易,而且其输出比你上面提到的其他工具更容易分析。
祝好运!

1
这不完全是我要找的。我不想要线程被取消调度的样本,那通常只会显示IO等待或带有空工作队列的线程池的堆栈。我想要在CPU上的样本,但仅在运行少于N个线程时。或者按空闲核心数量加权的样本。基本上它必须回答问题“哪些调用堆栈代表单线程,我必须加速或消除以返回并行执行”。即使唤醒程序部分只显示唤醒的立即堆栈,而不是所有必须完成的前面计算。 - the8472
@the8472:在超线程的Intel CPU上,有一个cpu_clk_unhalted.one_thread_active的性能计数器,当一个线程拥有一个核心时会打勾。这可以帮助找到在利用不到最大核心数时运行的代码。还有Linux的perf事件,如cstate_core/c3-residency/和c6、c7,因此您可能能够计算整个核心空闲了一段时间。 (但我认为这不能帮助您找到正在其他核心上运行的代码。) - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接