如何优先处理(或设置调度策略)进程的“经理”和“工作者”线程?

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

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

有可能是超额订阅(即多个工作线程的数量比使用HT的英特尔处理器的内核数量的两倍多)。 现在,我所看到的是'经理'线程不足以获得处理器时间。 他们并不完全“饿死”,我只是想给他们一个提振。 所以,当然我想过设置不同的线程优先级(我在Linux上) – 但后来我注意到线程调度程序及其效果的不同select。 在这一点上我感到困惑,或者说 – 我不清楚:

  • 我应该为pipe理者select哪种调度政策,哪些为工作人员select?
  • 我应该如何设置线程优先级(如果有的话)?
  • 我需要有我的线程yield()偶尔?

笔记:

  • 我故意不说有关语言或线程池机制的任何事情。 我想在更一般的情况下提出这个问题。
  • 请不要对CPU核心做出假设。 可能有很多,也可能只有一个,也许我需要每个核心的工人(或工人和经理)。
  • 工作线程可能会或可能不会做I / O。 尽pipe如此,他们还是没有做任何I / O的答案。
  • 除了运行我的应用程序之外,我并不需要系统的响应速度非常快。 我的意思是,我宁愿能够在那里SSH,让我的键入没有显着的延迟回应,但没有真正的限制。

    UPD 12.02.2015 :我已经运行了一些实验。

    理论

    将“管理器”线程调度程序更改为RT(提供SCHED_DEADLINE / SCHED_FIFO策略的实时调度程序)显然是一个解决方案。 在这种情况下,“管理者”线程总是比系统中的大多数线程具有更高的优先级,所以他们几乎总是在需要的时候获得CPU。

    不过,还有另外一个解决方案可以让你继续使用CFS调度程序 。 您对“工人”线程目的的描述类似于批处理调度(在古代,当计算机很大时,用户必须把工作放在队列中等待几个小时直到完成)。 Linux CFS支持通过SCHED_BATCH策略和对话作业通过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; 

    所以当“经理”线程或其他事件唤醒“工作者”时,只有当系统中有空闲的CPU或“管理器”将耗尽其时间片(调整它改变任务的权重)时,CPU才会获得CPU。

    看来你的问题不能在不改变调度策略的情况下解决。 如果“工人”线程非常繁忙,“经理人”很少醒来,他们会得到相同的vruntime奖金,所以“工人”总是会抢占“经理”线程(但是你可能会增加自己的体重, )。

    实验

    我有一个服务器与2个英特尔至强E5-2420 CPU给我们24硬件线程。 为了模拟两个线程池,我使用了我自己的TSLoad工作负载生成器(并在运行实验时修复了几个错误:))。

    有两个线程tp_worker :具有4个线程的tp_worker和具有30个线程的tp_worker ,两者都运行busy_wait工作负载(仅for(i = 0; i < N; ++i); ),但循环次数不同。 tp_workerbenchmark模式下工作,因此它可以运行尽可能多的请求,并占用100%的CPU。

    这里是示例配置: https : //gist.github.com/myaut/ad946e89cb56b0d4acde

    3.12(香草调试配置)

     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 

    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 

    一些说明:

    • 所有的时间都是毫秒
    • 最后一个实验是设置亲和性(由@PhilippClaßen建议):管理线程绑定到核心#0,而工作线程绑定到除核心#0之外的所有核心。
    • 经理线程的服务时间增加了两倍,这可以通过核心内部的并发来解释(处理器具有超线程技术!)
    • 使用SCHED_BATCH + nice(TSLoad不能直接设置权重,但nice可以间接执行)略微减少等待时间。
    • SCHED_FIFO实验中的负等待时间是OK:TSLoad保留30us,所以它可以做前期工作/调度程序有时间做上下文切换/等等。似乎SCHED_FIFO是非常快的。
    • 保留单核并没有那么差,而且由于它去除了内核中的并发性,服务时间明显减少

    除了myaut的回答之外,您还可以将管理器绑定到特定的CPU( sched_setaffinity ),将工作者绑定到其他人员。 当然,这取决于您的确切用例,这可能非常浪费。

    链接: 线程绑定的CPU核心

    显然的屈服通常是不必要的,实际上通常是不鼓励的。 在“Linux系统编程”中引用Robert Love:

    实际上,在适当的抢占式多任务系统(如Linux)上sched_yield()的合法用法很少。 内核完全有能力做出最佳和最有效的调度决策 – 当然,内核比单个应用程序更好地装备,以决定什么时候抢占。

    他提到的例外是当你等待外部事件时,例如由用户,硬件或其他进程引起的事件。 在你的例子中情况并非如此。

    除了myaut的优秀答案之外,是考虑尝试一个应用了CONFIG_PREEMPT_RT补丁集的内核。 这对内核如何调度做出了一些相当大的改变,最终的结果是调度延迟变得更加确定。

    结合使用myaut的建议(尤其是使用SCHED_FIFO)获得相对线程优先级的正确方法(管理员>工作人员)可以产生非常好的结果。