如何确定流程的"经理"和"工作线程"线程的优先级(或为其设置调度策略)?
How to prioritize (or set scheduling policy for) the 'manager' and 'worker' threads of a process?
我正在运行一个进程(在基于Linux 3.x的操作系统上(,其中:
- 少数线程是"管理器"线程(为了简单起见,假设它们决定哪些工作线程应该做什么,但不做任何I/O,并且它们所需的CPU时间总共比工作线程短/短得多(
- 更多的线程是"工作者"线程:它们在计算方面做繁重的工作,我对它们在任何时候被抢占都没有问题
可能存在超额订阅(即,使用HT的英特尔处理器上的工作线程数是内核数的两倍多(。现在,我看到的是"管理器"线程没有足够频繁地获得处理器时间。他们并没有完全"饿死",我只是想给他们打气。所以,我自然会考虑设置不同的线程优先级(我在Linux上(,但后来我注意到线程调度器的不同选择及其效果。在这一点上,我感到困惑,或者更确切地说,我不清楚:
- 我应该为经理选择哪种调度策略,为工人选择哪种
- 我应该将线程优先级设置为什么(如果有的话(
- 我需要偶尔让我的线程yield((吗
注意:
- 我有意不对语言或线程池机制做任何说明。我想在更普遍的背景下提出这个问题
- 请不要对CPU核心进行假设。他们可能有很多,也可能只有一个,也许我需要每个核心的员工(或员工和经理(
- 工作线程可以执行I/O,也可以不执行I/O。不过,对于他们不执行任何I/O的情况,欢迎回答
- 除了运行我的应用程序之外,我真的不需要系统的响应能力很强。我的意思是,我宁愿能够在那里进行SSH,并让我的输入在没有显著延迟的情况下得到响应,但没有真正的限制
UPD 12.02.2015:我做了一些实验。
理论
有一个明显的解决方案可以将"管理器"线程调度程序更改为RT(提供SCHED_DEADLINE/SCHED_FIFO策略的实时调度程序(。在这种情况下,"管理器"线程总是比系统中的大多数线程具有更大的优先级,因此它们几乎总是在需要CPU时获得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,或者当"管理者"耗尽其时间片时(调整它会改变任务的权重(。
如果不更改调度程序策略,您的问题似乎无法解决。如果"工作者"线程非常繁忙,而"管理者"很少醒来,那么他们将获得相同的vruntime
奖金,因此"工作者"将始终优先于"管理器"线程(但您可能会增加它们的权重,因此它们会更快地耗尽奖金(。
实验
我有一台带有2个Intel Xeon E5-2420 CPU的服务器,它为我们提供了24个硬件线程。为了模拟两个线程池,我使用了自己的TSLoad工作负载生成器(并在运行实验时修复了几个错误:(。
有两个线程池:tp_manager
有4个线程,tp_worker
有30个线程,都运行busy_wait
工作负载(只有for(i = 0; i < N; ++i);
(,但循环周期数不同。tp_worker
在benchmark
模式下工作,因此它将运行尽可能多的请求,并占用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建议(:管理线程绑定到Core#0,而工作线程绑定到除Core[0]以外的所有核心
- 管理器线程的服务时间增加了两倍,这可以通过内核内部的并发来解释(处理器有超线程!(
- 使用SCHED_BACH+nice(TSLoad不能直接设置权重,但
nice
可以间接设置(略微减少了等待时间 - SCHED_FIFO实验中的负等待时间是可以的:TSLoad保留了30us,这样它就可以做前期工作/调度器有时间做上下文切换等。看起来SCHED_FFIFO非常快
- 保留单个核心并没有那么糟糕,而且由于它消除了核心内的并发性,服务时间显著减少
除了myout的答案之外,您还可以将管理器绑定到特定的CPU(sched_setafinity(,将工作程序绑定到其他CPU。当然,根据您的具体用例,这可能会非常浪费。
链接:线程绑定CPU核心
明确的让步通常是不必要的,事实上常常是不鼓励的。引用Robert Love在"Linux系统编程"中的话:
在实践中,在Linux等适当的抢占式多任务系统上,很少有合法使用sched_yield((的情况。内核完全能够做出最佳和最有效的调度决策——当然,内核比单个应用程序更适合决定抢占什么以及何时抢占。
他提到的例外情况是,当您正在等待外部事件时,例如,由用户、硬件或其他进程引起的事件。在你的例子中,情况并非如此。
myout的一个优秀答案是考虑尝试应用CONFIG_PREEMPT_RT补丁集的内核。这对内核的调度方式进行了一些相当重大的更改,最终结果是调度延迟变得更加具有确定性。
与myout的任何一个建议(尤其是与SCHED_FIFO(结合使用,可以获得非常好的结果。
- 将更高的优先级设置为 boost::asio 线程处理进程
- 是否可以在单独的线程中将 QObject 设置为 QML 上下文属性?
- 如何使用 pthreads 以正确的方式设置两个线程之一的优先级
- 设置 JVM 通过 JNI 初始化时创建的线程数
- 设置提升记录器的线程名称
- Android Ndk - 为C++线程设置名称
- 如何设置"this"线程的自定义名称?
- 如何在 OpenMP 中设置线程数
- 在 Visual Studio 中的调试中断时设置默认线程,C++
- OpenBLAS 只为一个例程设置线程数
- 在iOS设备上执行并发任务时如何设置正确的线程数?
- 如何在不需要 root 访问权限的情况下为应用程序中的线程设置相对线程优先级
- 在C 多线程应用程序中,设置了并发线程的最大数量
- 在 OpenCV 上设置线程相关性
- 如何在 Qt5 中设置线程的 CPU 关联?
- 一个线程设置成员,而另一个循环上方 - 是此螺纹 - 不安全
- 不使用线程时,从不同的线程设置Qt父级
- 为线程设置新优先级时不允许操作
- 跨多个线程设置表项
- 从创建线程设置线程的"nice level"?