我们希望将服务器上的操作系统从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 处理器,运行频率为3.6 GHz,具有超线程,speedtep和所有C状态关闭) . 测试应用程序更改线程的亲和力以在同一套接字的独立物理核心上运行它们(即,第一个线程在Core 0上运行,第二个线程在Core 1上运行),因此没有线程的弹跳套接字之间的核心或弹跳/通信 .
两台主机之间的唯一区别是,一台运行Ubuntu 10.04 LTS,内核为2.6.32-28(快速上下文切换盒),另一台运行最新的Ubuntu 12.04 LTS,内核为3.2.0-23(缓慢的上下文)开关盒) . 所有BIOS设置和硬件都相同 .
内核是否有任何变化可以解释线程被安排运行多长时间的这种荒谬的减速?
Update: 如果您想在主机和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
Update 2 :经过大量内核参数搜索,内核更改和个人研究的帖子后,我已经找出了问题所在并发布了解决方案作为这个问题的答案 .
3 回答
在最近的内核中,坏线程唤醒性能问题的解决方案与从
acpi_idle
(旧内核中使用的驱动程序)切换到intel_idle
cpuidle驱动程序有关 . 遗憾的是,intel_idle
驱动程序忽略用户的C状态的BIOS配置并跳转到自己的曲调 . 换句话说,即使你完全禁用了你的PC中的所有C状态,这个驱动程序仍然会在短暂的不活动期间强行打开它们,这几乎总是发生,除非所有核心消耗合成基准(例如,压力)在跑 . 您可以使用大多数兼容硬件上的精彩Google i7z tool来监控C状态转换以及与处理器频率相关的其他有用信息 .要查看您的设置中当前处于活动状态的cpuidle驱动程序,只需在
/sys/devices/system/cpu
的cpuidle
部分中输入current_driver
文件,如下所示:如果您希望现代Linux操作系统具有最低的上下文切换延迟,请添加以下内核启动参数以禁用所有这些省电功能:
在Ubuntu 12.04上,您可以通过将它们添加到
/etc/default/grub
中的GRUB_CMDLINE_LINUX_DEFAULT
条目然后运行update-grub
来完成此操作 . 要添加的引导参数是:以下是三个启动选项的详细信息:
将
intel_idle.max_cstate
设置为零将将cpuidle驱动程序还原为acpi_idle
(至少根据选项的文档),或者完全禁用它 . 在我的盒子上它被完全禁用(即,在/sys/devices/system/cpu/cpuidle
中显示current_driver
文件产生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
是一个真正的强大的力量 . 它将禁用C1 / C1E,这将消除最后剩余的延迟时间,代价是更多的功耗,所以只有当它在性能方面的改进与实际的功耗/热量的增加时才使用它硬件和权衡权衡 .Update:
在使用各种
idle=*
参数进行额外测试后,我发现如果硬件支持将idle
设置为mwait
则更好 . 似乎使用MWAIT/MONITOR
指令允许CPU进入C1E,而不会在线程唤醒时间中添加任何明显的延迟 . 使用idle=mwait
,您将获得更低的CPU温度(与idle=poll
相比),更少的功耗并仍然保持轮询空闲循环的出色低延迟 . 因此,基于这些发现,针对低CPU线程唤醒延迟的更新推荐的引导参数集是:使用
idle=mwait
而不是idle=poll
也可以帮助启动Turbo Boost(通过帮助CPU保持低于其TDP [热设计功率])和超线程(MWAIT是不使用整个物理内核的理想机制 . 同时避免较高的C状态) . 然而,这在测试中尚未得到证实,我将继续这样做 .Update 2:
mwait
idle选项已removed from newer 3.x kernels(感谢用户ck_进行更新) . 这给我们留下了两个选择:idle=halt
- 应该和mwait
一样工作,但要测试以确保您的硬件是这种情况 .HLT
指令几乎相当于带有状态提示0的MWAIT
. 问题在于需要中断才能退出HLT状态,而内存写入(或中断)可用于退出MWAIT州 . 根据Linux内核在其空闲循环中使用的内容,这可以使MWAIT更有效 . 所以,正如我所说的测试/配置文件,看它是否满足您的延迟需求......和
idle=poll
- 性能最高的选择,牺牲功率和热量 .也许变慢的是futex,它是条件变量的构建块 . 这将有所启发:
然后
这将显示有趣的系统调用所采用的微秒,按时间排序 .
在内核2.6.32上
在内核3.1.9上
我发现这个5 year old bug report包含一个比较的"ping pong"性能测试
单线程libpthread互斥锁
libpthread条件变量
普通的旧Unix信号
我不得不补充一下
为了编译,我用这个命令做了
在内核2.6.32上
在内核3.1.9上
我得出结论,在内核2.6.32和3.1.9之间,上下文切换确实放慢了速度,尽管没有你在内核3.2中观察到的那么多 . 我意识到这还没有回答你的问题,我会继续挖掘 .
编辑:我发现更改进程的实时优先级(两个线程)可以提高3.1.9的性能以匹配2.6.32 . 但是,在2.6.32上设置相同的优先级会让它变慢...去图 - 我会更多地研究它 .
这是我现在的结果:
在内核2.6.32上
在内核3.1.9上
您可能还会看到处理器在更新的进程和Linux内核中单击,因为 pstate 驱动程序与c状态分开 . 所以另外,要禁用它,你需要以下内核参数:
intel_pstate=disable