为什么Linux的调度程序将两个线程放在具有超线程的处理器上的同一物理内核上?

nh2*_*nh2 18 linux performance multithreading scheduler

我已经在多个地方读过Linux的默认调度程序在多核机器上的超线程感知,这意味着如果你有一台具有2个真实内核(4 HT)的机器,它将不会以某种方式将两个忙线程安排到逻辑内核上它们都运行在相同的物理内核上(在许多情况下会导致2倍的性能成本).

但是当我stress -c 2在我的Intel i5-2520M上运行(产生两个线程以在100%CPU上运行)时,它经常将两个线程调度(并保持)到HT核心1和2上,这些核心映射到相同的物理核心.即使系统处于空闲状态.

这也适用于真正的程序(我在stress这里使用它因为它很容易重现),当发生这种情况时,我的程序可以理解地需要两倍的时间来运行.手动设置亲和力与taskset我的程序的修复程序,但我希望HT感知调度程序自己正确地执行此操作.

您可以找到HT->物理核心配置egrep "processor|physical id|core id" /proc/cpuinfo | sed 's/^processor/\nprocessor/g'.

所以我的问题是:为什么调度程序将我的线程放在同一个物理内核上?


笔记:

  • 这个问题与其他问题非常相似,答案就是说Linux有一个非常复杂的线程调度程序,它具有HT意识.如上所述,我无法观察到这一事实(请自行检查stress -c),并想知道原因.
  • 我知道我可以手动为我的程序设置处理器亲和性,例如使用taskset工具或sched_setaffinity函数.这不是我正在寻找的,我希望调度程序能够自己知道将两个忙线程映射到物理核心并将一个物理核心完全留空并不是一个好主意.
  • 我知道在某些情况下你更喜欢将线程安排到同一个物理内核上并让其他内核保持空闲,但调度程序大约1/4的情况下,这似乎是荒谬的.在我看来,它所选择的HT核心是完全随机的,或者可能是那些在调度时活动最少的HT核心,但这并不是非常超线程的意识,考虑到具有stress受益于在不同的物理核心上运行.

mya*_*aut 7

我认为是时候从评论中总结一些知识了.

Linux调度程序知道超线程 - 有关它的信息应该从BIOS/UEFI提供的ACPI SRAT/SLIT表中读取 - 而不是Linux 从中构建调度程序域.

域具有层次结构 - 即在2 CPU服务器上,您将获得三层域:all-cpus,per-cpu-packageper-cpu-core域.您可以从/proc/schedstat以下位置查看:

$ awk '/^domain/ { print $1, $2; } /^cpu/ { print $1; }' /proc/schedstat
cpu0
domain0 0000,00001001     <-- all cpus from core 0
domain1 0000,00555555     <-- all cpus from package 0
domain2 0000,00ffffff     <-- all cpus in the system
Run Code Online (Sandbox Code Playgroud)

CFS调度程序的一部分是负载均衡器 - 应该将任务从繁忙的核心窃取到另一个核心的野兽.以下是来自内核文档的描述:

在执行此操作时,它会检查当前域是否已用尽其重新平衡间隔.如果是这样,它将load_balance()在该域上运行.然后它检查父sched_domain(如果存在)和父级的父级,依此类推.

最初,load_balance()找到当前sched域中最繁忙的组.如果成功,它会查找该组中所有CPU的运行队列中最繁忙的运行队列.如果它设法找到这样的runqueue,它会锁定我们的初始CPU的runqueue和新发现的最繁忙的runqueue,并开始将任务从它移动到我们的runqueue.在迭代这个sched域的组时,确切的任务数量相当于先前计算的不平衡.

来自:https://www.kernel.org/doc/Documentation/scheduler/sched-domains.txt

您可以通过比较数字来监控负载均衡器的活动/proc/schedstat.我写了一个脚本来做到这一点:schedstat.py

计数器alb_pushed显示负载均衡器已成功移出任务:

Sun Apr 12 14:15:52 2015              cpu0    cpu1    ...    cpu6    cpu7    cpu8    cpu9    cpu10   ...
.domain1.alb_count                                    ...      1       1                       1  
.domain1.alb_pushed                                   ...      1       1                       1  
.domain2.alb_count                              1     ...                                         
.domain2.alb_pushed                             1     ...
Run Code Online (Sandbox Code Playgroud)

但是,负载均衡器的逻辑很复杂,因此很难确定哪些原因可以阻止它正常工作以及它们与schedstat计数器的关系.我和@thatotherguy都不能重现你的问题.

我看到了这种行为的两种可能性:

  • 您有一些积极的节电策略,试图节省一个核心,以减少CPU的功耗.
  • 你真的遇到了一个调度子系统的错误,你应该去LKML并仔细分享你的发现(包括mpstatschedstat数据)


tha*_*guy 5

我无法使用我的Intel®Xeon®CPU E5-1650 0 @ 3.20GHz在3.13.0-48上重现此错误。

我有6个具有超线程的核心,其中逻辑核心N映射到物理核心N mod 6。

这是topwith 的典型输出,stress -c 4分为两列,因此每一行都是一个物理核心(由于系统未处于空闲状态,我省略了几个核心):

%Cpu0  :100.0 us,   %Cpu6  :  0.0 us, 
%Cpu1  :100.0 us,   %Cpu7  :  0.0 us, 
%Cpu2  :  5.9 us,   %Cpu8  :  2.0 us, 
%Cpu3  :100.0 us,   %Cpu9  :  5.7 us, 
%Cpu4  :  3.9 us,   %Cpu10 :  3.8 us, 
%Cpu5  :  0.0 us,   %Cpu11 :100.0 us, 
Run Code Online (Sandbox Code Playgroud)

这是在杀死并重新启动之后stress

%Cpu0  :100.0 us,   %Cpu6  :  2.6 us, 
%Cpu1  :100.0 us,   %Cpu7  :  0.0 us, 
%Cpu2  :  0.0 us,   %Cpu8  :  0.0 us, 
%Cpu3  :  2.6 us,   %Cpu9  :  0.0 us, 
%Cpu4  :  0.0 us,   %Cpu10 :100.0 us, 
%Cpu5  :  2.6 us,   %Cpu11 :100.0 us, 
Run Code Online (Sandbox Code Playgroud)

我做了几次,没有看到在12个逻辑核心上有4个线程在同一物理核心上进行调度的任何实例。

随着-c 6我趋向于获得这样的结果,Linux似乎正在帮助在其自身的物理内核上调度其他进程。即使这样,它们的分布也比偶然更好:

%Cpu0  : 18.2 us,   %Cpu6  :  4.5 us, 
%Cpu1  :  0.0 us,   %Cpu7  :100.0 us, 
%Cpu2  :100.0 us,   %Cpu8  :100.0 us, 
%Cpu3  :100.0 us,   %Cpu9  :  0.0 us, 
%Cpu4  :100.0 us,   %Cpu10 :  0.0 us, 
%Cpu5  :100.0 us,   %Cpu11 :  0.0 us, 
Run Code Online (Sandbox Code Playgroud)