12

我正在尝试分析我在一台大型机器(32 核,256GB RAM)上编写的多线程程序。我注意到在两次运行之间,程序的性能可能会有很大差异(70-80%)。我似乎无法找到程序性能出现这种巨大差异的原因,但是通过分析大量运行的“时间”实用程序的结果,我注意到非自愿上下文切换的数量与程序性能(显然,更少的上下文切换会带来更好的性能,反之亦然)。

有什么好的方法可以确定是什么导致了这种上下文切换?如果我能找到罪魁祸首,那么也许我可以尝试解决问题。但是,我对可以使用的工具有一些特殊限制。首先,我在这台机器上没有 root 权限,所以任何需要这种权限的工具都没有了。其次,它是一个相当老的内核(RHEL5,内核 2.6.18),所以一些标准的性能事件可能不存在。无论如何,任何关于如何更深入地挖掘上下文切换原因的建议都将不胜感激。

更新:我决定在另一台(更小的)机器上测试我的程序。另一台机器是一个 4 核(带有超标题)的 linux 机器,具有 8Gb 的 RAM,以及一个更新的内核——另一台机器上的 3.2.0 与 2.6.18。在新机器上,我无法重现双模式性能配置文件。这让我相信这个问题要么是由于硬件问题(正如评论中所建议的那样),要么是由于内核级别的一个特别病态的情况,该情况已被修复。我目前最好的假设是,这可能是因为新机器有一个带有完全公平调度程序 (CFS) 的内核,而旧机器没有。有没有办法测试这个假设(告诉新机器使用不同/旧的调度程序)而不必为新机器重新编译一个古老的内核版本?

4

6 回答 6

8

您提到有 32 个内核,但硬件的确切布局是什么?例如,机器有多少个包,多少个内核,如何共享缓存等。对于共享此类信息,我个人喜欢共享likwid-topology -g.

无论如何,在您的运行中存在一种不确定性:线程亲和性。操作系统以某种方式分配 SW 线程以在特定的 HW 线程上运行,而不考虑线程如何通信的知识(只是因为它没有这些知识)。这可能会导致各种影响,因此对于可重现的运行,确保以某种方式将 SW 线程固定到 HW 线程是一个好主意(也可能有一种最佳方式,但到目前为止我只是在谈论确定性)。

对于固定(又名亲和力),您可以使用显式 Pthread 调用,或者您可以尝试 Likwid 套件中的另一个工具,称为likwid-pin- 请参见此处

如果这不能让您获得一致的结果,请在您的工作负载上运行一个良好的分析器(例如 Intel VTune),确保您捕获更快的运行和更慢的运行,然后比较结果。在 VTune 中,您可以使用并排显示两个配置文件的比较功能。

于 2013-07-02T04:44:33.837 回答
3

我相信你的问题实际上是一个调度问题。

无法避免自己的进程被 CPU 抢占,但问题是,如果线程被抢占,那么它的下一个量程最终会在不同的 CPU 上结束,或者更具体地说是在具有不同 L2 的 CPU 上缓存,那么它对内存的访问将全部产生缓存未命中并导致从内存中获取数据。另一方面,如果线程被调度在同一个 CPU 上,它的数据很可能在 cahce 上仍然可用,例如产生更快的内存访问。

请注意,当您拥有越来越多的核心时,最有可能发生这种行为。而且由于它是一种“随机”,你的线程最终会在它的下一个量子上结束,那么这可以解释性能的随机性。

有一些分析工具可以让你注册你的线程被安排在哪里,例如perf for Linux。通常,这些工具特定于您正在执行程序的拓扑。不幸的是,我现在没有想到其他人。还有一些方法可以告诉操作系统在同一个(或相邻的)CPU 上调度线程,因此它们将受益于更少的缓存未命中。为此,您可以检查这个 SO question

我建议您询问您的管理员您可以使用哪些此类工具,以便您可以对线程调度进行适当的分析和分配。

于 2013-07-01T09:36:59.807 回答
1

您可以使用ftrace(使用 sched_switch 跟踪器)跟踪内核中上下文切换的来源。此外,使用perf可以帮助您缩小其他指标(缓存未命中等)。

当您将程序从 1 个线程增加到 32 个(并且可能超过此数量)时,性能可变性如何变化?

可能是您的程序具有各个线程正在竞争的共享数据(或其他一些资源)吗?这样,当有更多的并行执行(因此可变性能)时,他们会遇到更大的竞争条件?除了 CPU 时间,你的程序线程还争抢什么资源?

我认为你将不得不在你的 4 核机器上编译旧内核。如果您还没有这样做,那么这样做不仅是一个很好的教育练习,而且对于隔离这个问题中的变量也很有价值。如果您要这样做,我也会尝试匹配确切的发行版,因为您的问题可能不仅限于内核。它也可能是用户空间中的某些东西,或者可能只是内核的编译选项中的东西(因此将在同一个发行版中匹配)。

您可能想考虑使用分析器,例如 gprof。它可以让您深入了解(潜在的)瓶颈。例如,如果您正在等待写入系统调用或类似的东西。

以下是一些可能有助于引发一两个想法的文章:

http://halobates.de/lk09-scalability.pdf

http://pdos.csail.mit.edu/papers/linux:osdi10.pdf

附加来源:为什么每秒进行一次非自愿上下文切换?

于 2013-06-28T05:09:52.453 回答
1

您提到的是在一台机器上而不是在另一台机器上看到的双模式性能配置文件。这很可怕,但这是正常的,即使对于单线程应用程序也是如此。

问题在于 Linux 系统(任何内核,无论使用何种调度程序)中影响应用程序性能的因素太多。它从地址随机化开始,以微观时间差异结束,最终导致进程之间出现巨大的上下文切换延迟。

Linux 不是实时系统。它只是试图在平均情况下尽可能高效。

您可以做很多事情来最小化性能差异:

将线程数减少到必要的最低限度。不要将问题的不同方面拆分为线程。仅在真正需要时拆分为线程,例如为 CPU 提供独立的(!)数字运算作业。尽量在一个线程中做尽可能多的因果联系工作。您的线程应该尽可能少地相互通信。特别是在延迟加起来的线程之间不应该有请求/响应模式。

假设您的操作系统每秒只能在线程/进程之间进行大约 1000 次上下文切换。这意味着每秒有 100 个请求/响应事务。如果您在 Linux 上进行基准测试,并且发现您可以做得更多,请忽略这一点。

尝试减少重要数据的内存占用。分布式数据往往会破坏缓存,对性能产生非常微妙且难以解释的影响。

于 2013-07-01T19:54:23.577 回答
0

当涉及到重新调度时,最好的办法是使用 Kernel Profiler/logger Ftrace。这应该向您显示其他线程正在抢占哪些线程。不幸的是,下半部中断处理程序没有正确标记,因此可能难以破译这些。

于 2013-07-02T09:18:31.103 回答
0

在多线程程序中,将线程附加到特定的 CPU 编号并检查您是否看到性能有所提高。

于 2013-07-02T05:26:34.850 回答