如何确定流程的"经理"和"工人"线程的优先级(或设置调度策略)?

ein*_*ica 5 c++ linux multithreading scheduling thread-priority

我正在运行一个进程(在基于Linux 3.x的操作系统上),其中:

  • 一些线程是'经理'线程(为简单起见,假设他们决定哪些工作线程应该做什么,但不做任何I/O和他们需要的CPU时间总共比工作者更短/更短线程)
  • 更多的线程是"工人"线程:它们在计算方面做得很重,而且我在任何时候被抢占都没有问题.

可能存在超额订购(即,使用HT的英特尔处理器上的工作线程数超过核心的两倍).现在,我看到的是'经理'线程没有足够频繁地获得处理器时间.他们并非完全'挨饿',我只是想给他们一个提升.所以,我自然而然地想到设置不同的线程优先级(我在Linux上) - 但后来我注意到线程调度程序的不同选择及其效果.此时我感到困惑,或者更确切地说 - 我不清楚:

  • 我应该为经理选择哪种调度策略,为工人选择哪种调度策略?
  • 我应该将线程优先级设置为什么(如果有的话)?
  • 我是否需要偶尔让我的线程产生()?

笔记:

  • 我故意不再谈论语言或线程池机制.我想在更一般的环境中提出这个问题.
  • 请不要对CPU核心做出假设.可能有很多,或者可能只有一个,也许我需要每个核心上的工人(或工人和经理).
  • 工作线程可能会也可能不会执行I/O. 但是,对于他们没有做任何I/O的情况的答案是受欢迎的.
  • 除了运行我的应用程序之外,我真的不需要系统响应很快.我的意思是,我宁愿能够在那里使用SSH,让我的打字回复给我,没有明显的延迟,但没有真正的限制.

mya*_*aut 7

UPD 12.02.2015:我做了一些实验.

理论

将"管理器"线程调度程序更改为RT(提供SCHED_DEADLINE/SCHED_FIFO策略的实时调度程序)有明显的解决方案.在这种情况下,"管理器"线程将始终具有比系统中大多数线程更大的优先级,因此它们几乎总是在需要时获得CPU.

但是,还有另一种解决方案可以让您继续使用CFS调度程序.您对"工人"线程的目的描述与批处理调度类似(在古代,当计算机很大时,用户必须将他的工作放到队列中并等待数小时才能完成).Linux CFS通过SCHED_BNCH策略和对话作业通过SCHED_NORMAL策略支持批处理作业.

内核代码中有一些有用的注释(kernel/sched/fair.c):

/*
 * Batch and idle tasks do not preempt non-idle tasks (their preemption
 * is driven by the tick):
 */
if (unlikely(p->policy != SCHED_NORMAL) || !sched_feat(WAKEUP_PREEMPTION))
    return;
Run Code Online (Sandbox Code Playgroud)

因此,当"管理器"线程或其他事件唤醒"工作者"时,后者只有在系统中有空闲CPU或"管理器"耗尽其时间片(调整它改变任务的权重)时才会获得CPU.

如果不更改调度程序策略,似乎无法解决您的问题.如果"工人"线程很忙并且"经理"很少醒来,他们会获得相同的vruntime奖金,因此"工人"总是会抢占"经理"线程(但你可能会增加他们的体重,所以他们会更快地耗尽他们的奖金) .

实验

我有一台带有2个Intel Xeon E5-2420 CPU的服务器,它为我们提供了24个硬件线程.为了模拟两个线程池,我使用了自己的TSLoad工作负载生成器(并在运行实验时修复了几个错误:)).

有两个tp_manager线程池:有4个线程和tp_worker30个线程,两个都运行busy_wait工作负载(只是for(i = 0; i < N; ++i);)但具有不同的循环周期数.tp_workerbenchmark模式下工作,因此它将尽可能多地运行请求并占用100%的CPU.

以下是示例配置:https://gist.github.com/myaut/ad946e89cb56b0d4acde

3.12(带调试配置的vanilla)

EXP  |              MANAGER              |     WORKER
     |  sched            wait    service | sched            service
     |  policy           time     time   | policy            time
33   |  NORMAL          0.045    2.620   |     WAS NOT RUNNING
34   |  NORMAL          0.131    4.007   | NORMAL           125.192
35   |  NORMAL          0.123    4.007   | BATCH            125.143
36   |  NORMAL          0.026    4.007   | BATCH (nice=10)  125.296
37   |  NORMAL          0.025    3.978   | BATCH (nice=19)  125.223
38   |  FIFO (prio=9)  -0.022    3.991   | NORMAL           125.187
39   |  core:0:0        0.037    2.929   | !core:0:0        136.719
Run Code Online (Sandbox Code Playgroud)

3.2(股票Debian)

EXP  |              MANAGER              |     WORKER
     |  sched            wait    service | sched            service
     |  policy           time     time   | policy            time
46   |  NORMAL          0.032    2.589   |     WAS NOT RUNNING
45   |  NORMAL          0.081    4.001   | NORMAL           125.140
47   |  NORMAL          0.048    3.998   | BATCH            125.205
50   |  NORMAL          0.023    3.994   | BATCH (nice=10)  125.202
48   |  NORMAL          0.033    3.996   | BATCH (nice=19)  125.223
42   |  FIFO (prio=9)  -0.008    4.016   | NORMAL           125.110
39   |  core:0:0        0.035    2.930   | !core:0:0        135.990
Run Code Online (Sandbox Code Playgroud)

一些说明:

  • 所有时间都以毫秒为单位
  • 最后一个实验用于设置亲和力(由@PhilippClaßen建议):经理线程绑定到Core#0,而工作线程绑定到除Core#0之外的所有核心.
  • 管理器线程的服务时间增加了两倍,这可以通过内核内的并发来解释(处理器具有超线程!)
  • 使用SCHED_BATCH + nice(TSLoad不能直接设置权重,但nice可以间接设置)稍微减少等待时间.
  • 在SCHED_FIFO实验中的负等待时间是可以的:TSLoad保留30us所以它可以做初步工作/调度器有时间进行上下文切换/等等.看起来SCHED_FIFO非常快.
  • 保留单核并不是那么糟糕,并且因为它消除了核心内并发性,所以服务时间显着减少