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

q8l4jmvw  于 11个月前  发布在  Linux
关注(0)|答案(4)|浏览(122)

我在多个地方读到过,Linux的默认调度程序在多核机器上是超线程感知的,这意味着如果你有一台机器有2个真实的核(4 HT),它不会将两个忙碌的线程调度到逻辑核上,使它们都在相同的物理核上运行(这在许多情况下会导致2倍的性能成本)。
但是当我在我的Intel i5- 2520 M上运行stress -c 2(产生两个线程在100% CPU上运行)时,它经常将(并保持)这两个线程调度到HT内核1和2上,它们Map到同一个物理内核
这种情况在真实的程序中也会发生(我在这里使用stress是因为它很容易重现),当这种情况发生时,我的程序运行的时间会增加一倍,这是可以理解的。使用taskset手动设置亲合性可以解决这个问题,但我希望HT感知调度器自己能正确地完成这一任务。
您可以找到 *HT->物理核心 * assgnation与egrep "processor|physical id|core id" /proc/cpuinfo | sed 's/^processor/\nprocessor/g'

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

备注:

  • 这个问题与other question非常相似,答案是 *Linux有一个非常复杂的线程调度器,它是HT感知的 *。如上所述,我无法观察到这个事实(用stress -c自己检查),我想知道为什么。
  • 我知道我可以为我的程序手动设置处理器亲和性,例如使用taskset工具或sched_setaffinity函数。这不是我想要的,我希望调度程序自己知道将两个忙碌Map到一个物理核心并让一个物理核心完全空着不是一个好主意。
  • 我知道在某些情况下,你更希望线程被调度到同一个物理核上,而让另一个核空闲,但调度器在大约1/4的情况下会这样做似乎是荒谬的。在我看来,它选择的HT核是完全随机的,或者可能是那些在调度时活动最少的HT核,但是,考虑到具有stress特性的程序从在单独的物理内核上运行中获得的好处,这并不是非常具有超线程意识的。
mm5n2pyu

mm5n2pyu1#

我想是时候从评论中总结一些知识了。
Linux调度程序知道超线程--有关它的信息应该从ACPI SRAT/SLIT表中读取,这些表由BIOS/UEFI提供--而Linux则从这些表中构建scheduler domains
域有层次结构--即在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

字符串
CFS调度器的一部分是负载均衡器--这头野兽应该从你的忙碌核心中窃取任务到另一个核心。下面是来自内核文档的描述:
在执行此操作时,它会检查当前域是否已用尽其重新平衡间隔。如果是,则在该域上运行load_balance()。然后检查父sched_domain(如果存在)以及父的父等。
最初,load_balance()在当前sched域中查找最繁忙的组。如果成功,则查找该组中所有CPU的运行队列中最繁忙的运行队列。如果它设法找到这样的运行队列,它锁定初始CPU的运行队列和新发现的最繁忙的运行队列,并开始将任务从它移动到我们的运行队列。在这个sched域的组。
From:https://www.kernel.org/doc/Documentation/scheduler/sched-domains.txt
您可以通过比较/proc/schedstat中的数字来监视负载均衡器的活动。
计数器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     ...


然而,负载均衡器的逻辑是复杂的,所以很难确定什么原因会阻止它正常工作,以及它们与schedstat计数器的关系。我和@thatotherguy都不能重现你的问题。
我认为这种行为有两种可能性:

  • 你有一些积极的节能策略,试图保存一个核心,以减少CPU的功耗。
  • 您确实遇到了调度子系统的错误,那么您应该转到LKML并仔细分享您的发现(包括mpstatschedstat数据)
kyks70gy

kyks70gy2#

我无法用我的英特尔(R)至强(R)CPU E5-1650 0@3.20 GHz在3.13.0-48上重现这一点。
我有6个超线程核心,逻辑核心NMap到物理核心N mod 6。
下面是top的典型输出,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,

字符串
以下是在杀死并重新启动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,


我这样做了几次,没有看到任何情况下,跨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,

qxsslcnc

qxsslcnc3#

我的猜测是,调度程序无法知道进程是否会从共享核或避免在同一核上运行中受益。这篇[1]文章表明,使用SMT可能会损害内存密集型工作负载,而CPU密集型工作负载可以从使用SMT中受益。这表明,使用所有虚拟核时,CPU密集型负载会表现得更好,而内存密集型的负载在每个物理核只使用1个虚拟核时会做得更好。2调度器显然假定大多数负载都是CPU限制的。
[1][https://www.phoronix.com/review/amd-epyc-9754-smt](https://www.phoronix.com/review/amd-epyc-9754-smt)

z0qdvdin

z0qdvdin4#

引用你的经验与两个额外的处理器,似乎工作正常,i7-2600和至强E5-1620;这可能是一个长远的机会,但如何CPU微码更新?它可以包括一些东西来修复问题,如果它的内部CPU行为。
Intel CPU Microcode Downloads:http://intel.ly/1aku6ak
参见:https://wiki.archlinux.org/index.php/Microcode

相关问题