linux - 在新的 linux 内核中上下文切换要慢得多

我们希望将我们服务器上的操作系统从 Ubuntu 10.04 LTS 升级到 Ubuntu 12.04 LTS。不幸的是,从 2.6 内核到 3.2 内核,运行已变为可运行的线程的延迟似乎显着增加。事实上,我们得到的延迟数字令人难以置信。

让我更具体地说明测试。我们有一个运行两个线程的程序。第一个线程获取当前时间(使用 RDTSC 以滴答为单位),然后每秒发送一次条件变量信号。第二个线程等待条件变量并在收到信号时唤醒。然后它获取当前时间(使用 RDTSC 以滴答为单位)。计算第二个线程中的时间与第一个线程中的时间之间的差异并显示在控制台上。此后,第二个线程再次等待条件变量。大约第二次通过后,第一个线程将再次发出信号。

因此,简而言之,我们通过每秒一次的条件变量延迟测量获得一个线程到线程的通信。

在内核 2.6.32 中,此延迟大约为 2.8-3.5 us,这是合理的。在内核 3.2.0 中,此延迟已增加到大约 40-100 us。我已经排除了两台主机之间硬件的任何差异。它们运行在相同的硬件上(双插槽 X5687 {Westmere-EP} 处理器以 3.6 GHz 运行,超线程、speedstep 和所有 C 状态都关闭)。测试应用程序更改线程的亲和性以在同一个套接字的独立物理内核上运行它们(即,第一个线程在内核 0 上运行,第二个线程在内核 1 上运行),因此不会出现线程反弹内核或套接字之间的弹跳/通信。

两台主机之间的唯一区别是,一台运行带有内核 2.6.32-28(快速上下文切换框)的 Ubuntu 10.04 LTS,另一台运行带有内核 3.2.0-23(慢速上下文)的最新 Ubuntu 12.04 LTS开关盒)。所有 BIOS 设置和硬件都是相同的。

内核中是否有任何更改可以解释调度线程需要多长时间运行的这种荒谬的减慢?

更新:
如果你想在你的主机和 linux 版本上运行测试,我有 posted the code to pastebin供您阅读。编译:

g++ -O3 -o test_latency test_latency.cpp -lpthread

运行(假设您至少有一个双核盒子):
./test_latency 0 1 # Thread 1 on Core 0 and Thread 2 on Core 1

更新 2 :
在对内核参数、内核更改帖子和个人研究进行了大量搜索之后,我已经弄清楚了问题所在并将解决方案发布为该问题的答案。

最佳答案

最近内核中坏线程唤醒性能问题的解决方案与切换到intel_idle有关。来自 acpi_idle 的 cpudle 驱动程序,旧内核中使用的驱动程序。可悲的是,intel_idle驱动程序会忽略用户的 C 状态的 BIOS 配置并按照自己的节奏跳舞。换句话说,即使您在 PC(或服务器)的 BIOS 中完全禁用所有 C 状态,该驱动程序仍将在短暂不事件期间强制它们打开,这几乎总是发生,除非所有核心消耗综合基准测试(例如,压力) 正在运行。您可以使用精彩的 Google i7z tool 监视 C 状态转换以及与处理器频率相关的其他有用信息。在大多数兼容的硬件上。

要查看您的设置中当前哪个 cpuidle 驱动程序处于事件状态,只需输入 current_driver cpuidle 中的文件/sys/devices/system/cpu的部分如下:

cat /sys/devices/system/cpu/cpuidle/current_driver

如果您希望现代 Linux 操作系统具有尽可能低的上下文切换延迟,请添加以下内核启动参数以禁用所有这些节能功能:

在 Ubuntu 12.04 上,您可以通过将它们添加到 GRUB_CMDLINE_LINUX_DEFAULT 中来做到这一点。进入 /etc/default/grub然后运行 ​​update-grub .要添加的引导参数是:
intel_idle.max_cstate=0 processor.max_cstate=0 idle=poll

以下是有关三个引导选项的作用的详细信息:

设置 intel_idle.max_cstate为零会将您的 cpuidle 驱动程序恢复为 acpi_idle (至少根据该选项的文档),或完全禁用它。在我的盒子上,它完全被禁用(即,在 current_driver 中显示 /sys/devices/system/cpu/cpuidle 文件会产生 none 的输出)。在这种情况下,第二个引导选项,processor.max_cstate=0是不必要的。但是,文档指出将 intel_idle 的 max_cstate 设置为零。驱动程序应该将操作系统恢复到 acpi_idle司机。因此,我添加了第二个引导选项以防万一。
processor.max_cstate选项设置 acpi_idle 的最大 C 状态驱动程序为零,希望也禁用它。我没有可以对此进行测试的系统,因为 intel_idle.max_cstate=0完全淘汰了我可用的所有硬件上的 cpuidle 驱动程序。但是,如果您的安装确实使您从 intel_idle 恢复至 acpi_idle只有第一个引导选项,如果第二个选项,请告诉我,processor.max_cstate做了评论中记录要做的事情,以便我可以更新这个答案。

最后,三个参数中的最后一个,idle=poll是一个真正的权力 pig 。它将禁用 C1/C1E,这将以更多的功耗为代价消除最后剩余的延迟位,因此仅在真正需要时才使用该延迟。对于大多数人来说,这将是矫枉过正,因为 C1* 延迟并不是那么大。使用我在原始问题中描述的硬件上运行的测试应用程序,延迟从 9 us 增加到 3 us。对于高度延迟敏感的应用程序(例如,金融交易、高精度遥测/跟踪、高频数据采集等)来说,这当然是一个显着的减少,但对于绝大多数应用程序而言,可能不值得遭受电力损失。桌面应用程序。唯一确定的方法是分析应用程序的性能改进与硬件功耗/热量的实际增加,并权衡利弊。

更新:

经过各种 idle=* 的额外测试参数,我发现设置idlemwait如果您的硬件支持是一个更好的主意。好像用了MWAIT/MONITOR指令允许 CPU 进入 C1E,而不会在线程唤醒时间中增加任何明显的延迟。与 idle=mwait ,您将获得更低的 CPU 温度(与 idle=poll 相比)、更少的电力使用并且仍然保持轮询空闲循环的出色低延迟。因此,我根据这些发现更新的低 CPU 线程唤醒延迟的推荐启动参数集是:
intel_idle.max_cstate=0 processor.max_cstate=0 idle=mwait
idle=mwait的使用而不是 idle=poll也可能有助于启动 Turbo Boost(通过帮助 CPU 保持在其 TDP [热设计功率] 以下)和超线程(为此 MWAIT 是不消耗整个物理内核同时避免更高 C州)。然而,这尚未在测试中得到证实,我将继续这样做。

更新 2:
mwait空闲选项已removed from newer 3.x kernels (感谢用户 ck_ 的更新)。这给我们留下了两个选择:
idle=halt - 应该和 mwait 一样有效,但请测试以确保您的硬件是这种情况。 HLT指令几乎等同于 MWAIT状态提示为 0。问题在于需要中断才能退出 HLT 状态,而内存写入(或中断)可用于退出 MWAIT 状态。根据 Linux 内核在其空闲循环中使用的内容,这可以使 MWAIT 潜在地更高效。所以,正如我所说的测试/配置文件,看看它是否满足您的延迟需求......


idle=poll - 最高性能选项,以牺牲功率和热量为代价。

https://stackoverflow.com/questions/12111954/

相关文章:

python - virtualenv和pyenv是什么关系?

python - 安装pycurl时出现"Could not run curl-config: [E

linux - CURL 用于访问需要从其他页面登录的页面

shell - 如何将文本 append 到文件?

linux - 如何使用 Red Hat Linux 上的标准工具随机化文件中的行?

python - 为什么我们不应该在 py 脚本中使用 sys.setdefaultencoding

linux - 确保只有一个 Bash 脚本实例正在运行的最佳方法是什么?

linux - 如何链接到特定的 glibc 版本?

python - 为什么函数可以修改调用者感知的某些参数,而不能修改其他参数?

python - 如何以正确的方式平滑曲线?