可视化每个线程正在运行的功能的工具

Posted

技术标签:

【中文标题】可视化每个线程正在运行的功能的工具【英文标题】:Tool to visualize what function each thread is running 【发布时间】:2013-07-01 10:19:15 【问题描述】:

我正在尝试调试多线程 C++ 应用程序的性能问题。基本上我的多线程程序(10 个线程)比单线程程序慢。

我一直在尝试 valgrind (callgrind)、gprof 和 gdb 等工具。但到目前为止,我无法弄清楚线程被阻塞的确切位置以及原因。 gprof 和 callgrind 让我在每个函数上花费了全部时间。但是这个时间是否包括线程被阻塞的时间?有没有开源工具可以用来调试这个问题。

【问题讨论】:

Profiling C++ multi-threaded applications 的可能副本。我建议您尝试 Intel 的 vtune。而且您似乎还没有充分利用 callgrind + hellgrind 可以提供的功能。 您只需要找到瓶颈和停机源吗?请尝试method described here。它是最简单、最快速的执行方式之一,并产生非常好的结果。尝试一下,即使它看起来“太微不足道而无法真正发挥作用”。请记住,它会让您找出等待的内容。它不会告诉你多长时间,什么在以及为什么。这些你必须阅读/思考你的代码。尽管如此,找到等待的东西通常是一个很好的开始。 提示:您可以使用 unix time 命令来估计上下文切换。 @quetzalcoatl 我做到了。它确实给了我一些指示。我还尝试使用 perf 工具,它指出内核调用 clear_page 是最常用的函数。我已经将我的问题指向每个线程(十万)大量对象的分配/释放。使用 tcmalloc 有点帮助,但性能不是我想要的。 【参考方案1】:

即使我没有现成的答案,我也会从这里的 cmets 切换,因为还有更多空间可以编写和格式化..

你能澄清一下“lahks”这个词吗?我只找到了something loosely related on Wiki,但这纯粹是猜测,我不明白你的意思。

你说的large number of objects per thread。当你随机采样/停止时,你看过堆栈跟踪吗?我知道 alloc/dealloc 是堆栈跟踪中最常见的 leaf,但是 *nonleaf*s 呢?您是否能够看到实际调用该 alloc/dealloc 的内容?这就是抽样方法的重点——查看调用的原始数据,并从统计上估计哪些可能的起源是导致调用频率过高的原因。

由于大量优化或架构不匹配(即,如果您的应用程序使用任务队列,那么大部分时间您只会看到“获取任务”,您可能无法观察堆栈跟踪的“较高部分” ","check task","execute task" 步骤而不是真正的起源),但几乎在每个架构中您都可以进行充分调整(就任务排队而言 - 只需尝试对任务注册进行抽样!)

还有另一种方式——alloc/dealloc 膨胀是相当普遍的:它通常与架构和算法有关,或者,好吧,错误。然而,这种事情不仅在“优化发布”构建中应该很容易观察到(在查看堆栈跟踪时存在问题),而且应该很快出现在“完整调试信息”构建中 - 整个系统的优化较少运行速度较慢,但​​您应该能够看到并收集所有可能来源的中间方法。

另一件事:您说过“多线程”的工作速度比“单线程”慢得多。这就产生了一个问题,即如何您能够在它们之间切换?你有两个单独的实现吗?还是您只是在 1 个工作线程和 N 个工作线程之间调整线程池大小?用“alloc/dealloc”问题解决这个问题——也许你的每个线程每次都需要执行太多的设置/拆卸?

尝试检查线程(作为一个组,也查看线程的生命周期)实际上必须重复与单线程选项相比准备什么。

例如,单线程以某种方式节省了 alloc/dealloc 并且可能重用了某些结构),而 N 线程可能需要 N 次相同的结构。如果线程只是重复启动/停止而不被重用,那么可能它们的 N*data 也没有被重用,因此 N 线程可能只是在实际工作之前的准备工作上消耗时间..

此外,如果您设法捕捉到无关的分配方案 - 为什么不进一步追踪:停止后,退出分配器并尝试查看正在分配的内容。我的意思是,您可以检查正在写入该内存的内容,这可以让您进一步了解实际发生的情况。然而,这可能是一项非常费力的任务,尤其是因为它必须重复很多次。我将它作为最后的手段。

另一件事是——纯粹是猜测——你的平台可能在 alloc/dealloc 中有一些全局锁,以“安全地跟踪”内存管理。这样,如果所有线程都按照自己的意愿管理自己的内存,那么线程将在每次内存分配/释放操作时相互等待。更改内存分配方案,或使用不同的内存管理器,或使用堆栈或 TLS,或将线程池拆分为单独的进程可能会有所帮助,因为它可以避免全局锁定的需要。但是,这只是一个非常遥远的猜测,没有一个解决方案是容易应用的。

对于这种笼统而含糊的谈话,我深表歉意。仅凭您提供的一些细节,很难说更多。我故意回避“可视化工作的工具”话题。如果您无法仅通过 sample/stop 方法看到正在发生的事情,那么所有可能的“线程可视化”工具很可能没有帮助:它们可能会向您显示与您现在看到的完全相同的内容,因为它们都分析相同的堆栈跟踪,只是比手动停止快一点..

【讨论】:

"lahks" 是一个错字。我的意思是 10 万(百万的 1/10)。从那以后我尝试过的东西很少。使用 google perf 工具中的 tcmalloc 使我的程序速度提高了 400% 以上,但仍然存在线程争用。 继续上一条评论 - 单线程需要 250 毫秒,而 4 个线程,每个线程执行相同数量的工作需要 970 毫秒。测试程序是这样的。文件中包含单词列表。当我运行时它有 1 个线程,它从文件中读取一个单词并在 1 个线程中处理。当我用 4 个线程运行它时,它从文件中获取 4 个单词并在单独的线程中处理每个单词。使用 gperftools 的 CPU 分析器对 CPU 进行分析使我想到了这个问题***.com/q/17524652/256400 抱歉这个幼稚的问题,但是“相同数量的工作”是指相同 25% 的原创作品,还是相同 100% 的原创作品..? 970ms 几乎是原始 250ms 的 4 倍,所以这可能意味着每个线程都在做一个完整的工作,并且线程已经完全序列化了它们的运行。但这会很奇怪,除非你忘记对数据进行分区,而且如果你在某个地方有一些完全阻止并行性的锁定.. 每个线程都在做相同数量的工作,100% 的原始工作。不涉及数据共享或隐式锁定。所以我有一个线程相互阻塞的问题,我正在尝试解决。【参考方案2】:

一种可能是您在单核 CPU 上运行多线程代码 :)

关于多线程的一个常见误解是,您只需将线程投入问题即可获得加速改进:这是错误的,除非您拥有真正的多核 CPU 和可并行化的问题(即可以拆分为独立可解决的问题)子问题)

也许您正在处理不可并行化的问题(例如哈希计算)或使用 I/O 访问(同样是不可并行化的)

【讨论】:

那是我最初的错误之一 :) 但后来我搬到了 4 核机器上,仍然是同一家商店。然而,使用 tcmalloc 帮助很大。并且使用 gpertools CPU profiler 让我问了这个问题 - ***.com/questions/17524652/…

以上是关于可视化每个线程正在运行的功能的工具的主要内容,如果未能解决你的问题,请参考以下文章

深入理解Java虚拟机——可视化监控工具(Jconsole)线程死锁监控示例

我可以使用 FLTK 1.3 在不同线程上创建窗口吗?

13.8.可视化虚拟机工具--Jconsole内存监控13.9.可视化虚拟机工具--Jconsole线程监控13.10.死锁原理以及可视化虚拟机工具--Jconsole线程

使用 htop 命令可视化 ruby​​ 线程

Redis 官方可视化工具,功能真心强大!

Redis 官方可视化工具,功能真心强大!