为什么即使有空闲 CPU 可用,SCHED_FIFO 线程也会分配给同一物理 CPU?

ter*_*ion 6 linux performance kernel scheduler hyperthreading

在调试我正在处理的应用程序中的一些性能问题时,我发现内核调度程序的奇怪行为。看起来繁忙的 SCHED_FIFO 任务往往会被调度到同一物理 CPU 的逻辑核心上,即使系统中有空闲的物理 CPU。

 8624 root     -81   0 97.0g  49g 326m R  100 52.7  48:13.06 26 Worker0 <-- CPU 6 and 26 
 8629 root     -81   0 97.0g  49g 326m R  100 52.7  44:56.26  6 Worker5 <-- the same physical core
 8625 root     -81   0 97.0g  49g 326m R   82 52.7  58:20.65 23 Worker1
 8627 root     -81   0 97.0g  49g 326m R   67 52.7  55:28.86 27 Worker3
 8626 root     -81   0 97.0g  49g 326m R   67 52.7  46:04.55 32 Worker2
 8628 root     -81   0 97.0g  49g 326m R   59 52.7  44:23.11  5 Worker4
Run Code Online (Sandbox Code Playgroud)

最初,线程在核心之间移动,但在某些时候,大多数 CPU 密集型线程最终会锁定在相同的物理核心上,并且似乎不会从那里移动。没有为工作线程设置关联性。

我尝试通过运行 12 个实例来使用合成负载来重现它:

chrt -f 10 yes > /dev/null &
Run Code Online (Sandbox Code Playgroud)

这是我得到的:

25668 root     -11   0  2876  752  656 R  100  0.0   0:17.86 20 yes
25663 root     -11   0  2876  744  656 R  100  0.0   0:19.10 25 yes
25664 root     -11   0  2876  752  656 R  100  0.0   0:18.79  6 yes
25665 root     -11   0  2876  804  716 R  100  0.0   0:18.54  7 yes
25666 root     -11   0  2876  748  656 R  100  0.0   0:18.31  8 yes
25667 root     -11   0  2876  812  720 R  100  0.0   0:18.08 29 yes <--- core9
25669 root     -11   0  2876  744  656 R  100  0.0   0:17.62  9 yes <--- core9
25670 root     -11   0  2876  808  720 R  100  0.0   0:17.37  2 yes 
25671 root     -11   0  2876  748  656 R  100  0.0   0:17.15 23 yes <--- core3
25672 root     -11   0  2876  804  712 R  100  0.0   0:16.94  4 yes
25674 root     -11   0  2876  748  656 R  100  0.0   0:16.35  3 yes <--- core3
25673 root     -11   0  2876  812  716 R  100  0.0   0:16.68  1 yes
Run Code Online (Sandbox Code Playgroud)

这是具有 20 个物理核心的服务器,因此还剩下 8 个空闲核心,并且线程仍然调度在同一个物理核心上。这是可重复且持久的。对于非 SCHED_FIFO 线程似乎不会发生这种情况。它也是在迁移过去的内核 4.19 之后开始的

对于 SCHED_FIFO 线程来说,这是正确的行为吗?是否有任何标志或配置选项可以更改此调度程序行为?

小智 1

如果我理解正确,您正在尝试启用SCHED_FIFO超线程(“HT”),这会导致每个物理核心有多个线程处理器。我的理解是,Linux 内核中的 HT 感知主要是通过 CFS(目前的默认调度程序)中的负载平衡和调度程序域。有关更多信息,请参阅/sf/answers/2071130561/ 。

使用SCHED_FIFOorSCHED_RR基本上会绕过 HT 处理,因为 RT 调度并不真正经过 CFS。

我过去处理这个问题的方法是禁用超线程。 对于您实际需要实时行为的情况,这通常是正确的延迟/性能权衡(请参阅https://rt.wiki.kernel.org/index.php/HOWTO:_Build_an_RT-application#Hyper_threading)。这是否合适实际上取决于您要解决的问题。

旁白:我怀疑如果你确实需要SCHED_FIFO行为,那么禁用 HT 就是你想要做的事情,但人们也很常见,因为SCHED_FIFO它是不适合工作的工具,所以他们认为他们需要。我怀疑可能有比使用更好的选择,SCHED_FIFO因为您描述的是在传统服务器而不是嵌入式系统上运行,但这是一个过于笼统的猜测。如果没有关于这个问题的更多细节,很难说。