【问题标题】:Why does Linux's scheduler put two threads onto the same physical core on processors with HyperThreading?为什么 Linux 的调度程序将两个线程放在具有超线程的处理器上的同一个物理内核上?
【发布时间】:2015-06-07 22:43:00
【问题描述】:

我在多个地方了解到 Linux 的默认调度程序在多核机器上超线程感知,这意味着如果您有一台具有 2 个真正核心 (4 HT) 的机器,它不会将两个繁忙的线程安排到逻辑核心上,使它们都运行在相同的物理核心上(这在许多情况下会导致 2 倍的性能成本)。

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

这也发生在实际程序中(我在这里使用stress,因为它很容易重现),当这种情况发生时,我的程序运行时间可以理解为两倍。使用taskset 手动设置关联可以为我的程序修复该问题,但我希望支持 HT 的调度程序能够自行正确执行此操作。

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

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


注意事项:

  • 这个问题与这个other question 非常相似,答案说Linux 有一个非常复杂的线程调度程序,它支持HT。如上所述,我无法观察到这一事实(请通过 stress -c 自行检查),并想知道原因。
  • 我知道我可以为我的程序手动设置处理器关联,例如使用taskset 工具或sched_setaffinity 函数。这不是我要寻找的,我希望调度程序自己知道将两个繁忙的线程映射到一个物理核心并让一个物理核心完全空着不是一个好主意。
  • 我知道在 some situations 中,您希望将线程调度到同一个物理核心上,而让另一个核心空闲,但调度程序会执行大约 1/4 的操作似乎很荒谬案例。在我看来,它选择的 HT 内核是完全随机的,或者可能是那些在调度时活动最少的 HT 内核,但是考虑到具有 @987654330 特征的程序的清晰程度,这对超线程不是很敏感@ 受益于在单独的物理内核上运行。

【问题讨论】:

  • 您指的是哪个发行版和版本?
  • 尝试在两个进程中运行压力,每个进程都有一个线程。我还没有研究过 Linux 调度程序的细节(自从我上次研究它以来,它甚至可能已经改变了)。出于缓存局部性等原因,内核可能更喜欢在同一物理处理器上的同一进程中调度线程。
  • Ubuntu 14.04,Linux 3.13.0。
  • @joshperry “内核可能更喜欢在同一个物理处理器上的同一个进程中调度线程”
  • Linux 通过调度程序域跟踪 HT 线程(以及最后一级缓存和 NUMA 节点)。请显示 awk '/^domain/ { print $1, $2; } /^cpu/ { print $1; }' /proc/schedstat 打印的内容,它将显示调度程序域的 CPU 掩码。

标签: linux multithreading performance scheduler


【解决方案1】:

我觉得是时候总结一下cmets的一些知识了。

Linux 调度程序了解超线程——有关它的信息应该从 BIOS/UEFI 提供的 ACPI SRAT/SLIT 表中读取——而不是 Linux 从中构建 scheduler domains。

域具有层次结构 -- 即在 2-CPU 服务器上,您将获得三层域:all-cpus、per-cpu-package 和 per-cpu-core em> 域。你可以通过/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 域的组时计算。

发件人: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     ...

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

我认为这种行为有两种可能性:

  • 您有一些激进的节能策略,试图节省一个内核以降低 CPU 的功耗。
  • 您确实遇到了调度子系统的错误,您应该转到LKML 并仔细分享您的发现(包括mpstat 和schedstat 数据)

【讨论】:

    【解决方案2】:

    我无法使用 Intel(R) Xeon(R) CPU E5-1650 0 @ 3.20GHz 在 3.13.0-48 上重现此问题。

    我有 6 个超线程内核,其中逻辑内核 N 映射到物理内核 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, 
    

    【讨论】:

    • 我刚刚在 Intel i7-2600 和 Intel Xeon E5-1620 上对其进行了测试,确实我得到了您描述的良好行为。但是在我的 Intel i5-2520M 上,我的调度行为很糟糕。我注意到不同的一件事是,在 i7 和 Xeon 上,我有一个 N mod 4 映射 (12341234),正如你所描述的,但在 i5 上我有一个“配对”映射 (1122)。可能有区别吗?
    【解决方案3】:

    引用您对两个似乎正常工作的额外处理器 i7-2600 和 Xeon E5-1620 的经验;这可能是一个长镜头,但 CPU 微码更新怎么样?如果是内部 CPU 行为,它可能包括解决问题的方法。

    Intel CPU 微码下载:http://intel.ly/1aku6ak

    另见此处:https://wiki.archlinux.org/index.php/Microcode

    【讨论】:

    • BIOS/UEFI 提供的 ACPI SRAT/SLIT 表中包含的关于核心和处理器关系的信息与 OP 问题无关。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多