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

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

因此,简而言之,我们得到每秒thread to thread communication via condition variable次的延迟测量结果.

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

这两台主机之间唯一的区别是,一台运行的是内核为2.6.32-28(快速上下文切换框)的Ubuntu10.04 LTS,另一台运行的是内核为3.2.0-23(慢速上下文切换框)的最新Ubuntu12.04 LTS.所有BIOS设置和硬件都是相同的.

内核中是否有任何变化可以解释线程运行所需时间的这种荒谬的减慢?

Update:

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

运行(假设您至少有一个双核机箱):

./test_latency 0 1 # Thread 1 on Core 0 and Thread 2 on Core 1

Update 2:

推荐答案

最近内核中bad thread wake up performance problem的解决方案与从acpi_idle切换到intel_idle cpuidle驱动程序有关,后者是旧内核中使用的驱动程序.遗憾的是,intel_idle驱动程序忽略了用户对C状态和dances to its own tune状态的BIOS配置.换句话说,即使您完全禁用PC(或服务器)BIOS中的所有C状态,该驱动程序仍会在短暂的不活动期间强制它们打开,除非运行一个全核心消耗的合成基准测试(例如压力测试),否则这种情况几乎总是会发生.在大多数兼容的硬件上,您可以使用精彩的Google i7z tool监控C状态转换,以及与处理器频率相关的其他有用信息.

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

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

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

在Ubuntu12.04上,你可以将它们添加到/etc/default/grub中的GRUB_CMDLINE_LINUX_DEFAULT条目中,然后运行update-grub.要添加的启动参数包括:

intel_idle.max_cstate=0 processor.max_cstate=0 idle=poll

以下是关于三个启动选项的详细信息:

intel_idle.max_cstate设置为零将使cpuidle驱动程序恢复为acpi_idle(至少根据选项的文档),或者完全禁用它.在我的盒子上,它被完全禁用(即,在/sys/devices/system/cpu/cpuidle中显示current_driver文件会产生none的输出).在这种情况下,第二个 bootstrap 选项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是一个真正的能量猪.它将禁用C1/C1E,这将删除最后剩余的延迟位,但会消耗更多的电量,因此只有在真正必要时才使用该选项.对于大多数人来说,这将是过度的,因为C1*延迟并没有那么大.使用我在原始问题中描述的硬件上运行的测试应用程序,延迟从9us增加到3us.对于对延迟高度敏感的应用程序(如金融交易、高精度遥测/跟踪、高频数据采集等),这无疑是一个显著的减少,但对于绝大多数桌面应用程序来说,这可能不值得遭受电力冲击.唯一可以确定的方法是,对比硬件功耗/热量的实际增加,分析应用程序在性能上的改进,并权衡权衡.

Update:

在使用各种idle=*个参数进行额外测试后,我发现如果硬件支持,将idle设置为mwait是一个更好的主意.似乎MWAIT/MONITOR指令的使用允许CPU进入C1E,而不会给线程唤醒时间增加任何明显的延迟.使用idle=mwait,CPU温度会更低(与idle=poll相比),功耗更低,并且仍然保持轮询空闲循环的出色低延迟.因此,基于这些发现,我更新了针对低CPU线程唤醒延迟的建议 bootstrap 参数集:

intel_idle.max_cstate=0 processor.max_cstate=0 idle=mwait

使用idle=mwait而不是idle=poll也可能有助于启动涡轮增压(通过帮助CPU保持低于其TDP[热设计功率])和超线程(对于超线程,MWAIT是不消耗整个物理内核的理想机制,同时避免更高的C状态).然而,这尚未在测试中得到证实,我将继续这样做.

Update 2:

mwait空闲选项为removed from newer 3.x kernels(感谢用户ck_u的更新).这给我们留下了两个 Select :

idle=halt-应该和mwait一样工作,但是测试以确保你的硬件是这样的.HLT指令几乎相当于状态提示为0的MWAIT指令.问题在于,中断需要脱离HLT状态,而内存写入(或中断)可以用于脱离MWAIT状态.根据Linux内核在空闲循环中使用的内容,这可能会使MWAIT更加高效.所以,正如我所说,测试/配置文件,看看它是否满足您的延迟需求...

idle=poll - The highest performance option, at the expense of power 和 heat.

Linux相关问答推荐

在程序集x86_64中跳转后调用ret时出现分段故障

Bash:将带有新行的字符串转换为带有逗号、内联的唯一值的字符串

Linux内核模块构建过程中,许可信息添加了前缀

Linux BlueZ 5.65 hcitool 结合服务 UUID 和制造数据广告

我可以在不调用 shell 的情况下使用 popen() 吗?在 C++ 中

为什么 liburing 写入性能低于预期?

仅在 Linux 上出现 AWS RDS `flush tables` 错误的 mysqldump

所有进程的Linux环境变量

如何使用 Bash 读取文件中的倒数第二行?

argv的编码是什么?

在 cron 作业(job)中执行 PHP 脚本

我如何从 Ubuntu 上的源代码自己构建 python?

grep 时间命令输出

在 linux 上查看文件大小

如何在字符串中查找子字符串(或如何 grep 变量)?

从 FTP 服务器下载所有文件

如何在 Linux 上查找不包含文本的文本文件?

PostgreSQL psql 终端命令

构建窗口管理器

在linux中根据内容拆分文件