可以使用sched_setaffinity
将线程固定到cpu上,从而提高性能(在某些情况下)
来自linux手册页:
限制进程在单个CPU上运行也可以避免 a时发生的缓存失效导致的性能开销 进程停止在一个CPU上执行,然后重新开始执行 一个不同的CPU
此外,如果我想要更实时的响应,我可以将该线程的调度程序策略更改为SCHED_FIFO
,并将优先级更高为某个高值(最多sched_get_priority_max
),这意味着有问题的线程应该总是抢占其cpu上运行的任何其他线程。
但是,此时,在实时线程刚刚抢占的cpu上运行的线程可能已经驱逐了大部分实时线程的一级缓存条目。
我的问题如下:
答案 0 :(得分:35)
答案是使用cpusets。 python cpuset utility可以轻松配置它们。
基本概念
3 cpusets
root
:出现在所有配置中并包含所有cpus(非屏蔽)system
:包含用于系统任务的cpus - 需要运行但不“重要”的(非屏蔽)user
:包含用于“重要”任务的cpus - 我们想要以“实时”模式运行的任务(屏蔽) shield
命令管理这3个cpusets。
在设置过程中,它将所有可移动任务移动到未屏蔽的cpuset(system
)中,并在拆解过程中将所有可移动任务移动到root
cpuset中。
设置完成后,子命令允许您将任务移动到 shield (user
)cpuset中,此外,还可以将特殊任务(内核线程)从root
移动到{{1} }(因此超出system
cpuset)。
<强>命令:强>
首先我们创造一个盾牌。当然,屏蔽的布局将取决于机器/任务。例如,假设我们有一个4核非NUMA机器:我们希望将 3个内核专用于屏蔽,并将 1个内核用于不重要的任务;因为它是非NUMA,我们不需要指定任何内存节点参数,我们让内核线程在user
cpuset中运行(即:跨所有cpu)
root
某些内核线程(未绑定到特定cpus的线程)可以移动到$ cset shield --cpu 1-3
cpuset中。 (通常,移动已绑定到特定cpu的内核线程并不是一个好主意)
system
现在让我们列出盾牌($ cset shield --kthread on
)或未屏蔽(user
)cpusets中运行的内容:( system
代表详细信息,列出进程名称)(添加第二个{ {1}}显示超过80个字符)
-v
如果我们想要停止盾牌(拆解)
-v
现在让我们在屏蔽中执行一个进程($ cset shield --shield -v
$ cset shield --unshield -v -v
之后的命令被传递给要执行的命令,而不是$ cset shield --reset
)
'--'
如果我们已经有一个正在运行的进程,我们想要进入屏蔽(注意我们可以通过传递逗号分隔列表或范围来移动多个进程(范围内的任何进程都将移动,即使存在间隙) )
cset
高级概念
$ cset shield --exec mycommand -- -arg1 -arg2
- 这些可以让你更好地控制cpusets
设置强>
创建,调整,重命名,移动和销毁cpusets
命令
使用cpus 1-3创建一个cpuset,使用NUMA节点1并将其命名为“my_cpuset1”
$ cset shield --shield --pid 1234
$ cset shield --shield --pid 1234,1236
$ cset shield --shield --pid 1234,1237,1238-1240
将“my_cpuset1”更改为仅使用cpus 1和3
cset set/proc
销毁cpuset
$ cset set --cpu=1-3 --mem=1 --set=my_cpuset1
重命名现有的cpuset
$ cset set --cpu=1,3 --mem=1 --set=my_cpuset1
创建分层cpuset
$ cset set --destroy --set=my_cpuset1
列出现有的cpusets(级别1的深度)
$ cset set --set=my_cpuset1 --newname=your_cpuset1
列出现有的cpuset及其子级
$ cset set --cpu=3 --mem=1 --set=my_cpuset1/my_subset1
列出所有现有的cpusets
$ cset set --list
<强> PROC 强>
管理线程和流程
命令
列出在cpuset中运行的任务
$ cset set --list --set=my_cpuset1
在cpuset中执行任务
$ cset set --list --recurse
移动任务
$ cset proc --list --set=my_cpuset1 --verbose
移动任务及其所有兄弟姐妹
$ cset proc --set=my_cpuset1 --exec myApp -- --arg1 --arg2
将所有任务从一个cpuset移动到另一个cpuset
$ cset proc --toset=my_cpuset1 --move --pid 1234
$ cset proc --toset=my_cpuset1 --move --pid 1234,1236
$ cset proc --toset=my_cpuset1 --move --pid 1238-1340
将未固定的内核线程移动到cpuset
$ cset proc --move --toset=my_cpuset1 --pid 1234 --threads
强行将内核线程(包括那些固定到特定cpu的线程)移动到cpuset中(注意:这可能会给系统带来可怕的后果 - 确保你知道你在做什么)
$ cset proc --move --fromset=my_cpuset1 --toset=system
层次结构示例
我们可以使用分层cpusets来创建优先级分组
$ cset proc --kthread --fromset=root --toset=system
cpuset
$ cset proc --kthread --fromset=root --toset=system --force
cpuset
system
cpuset
prio_low
cpuset
prio_met
cpuset(注意这与root相同;保持与root分离是个好习惯)要实现上述目的,请创建prio_all,然后在prio_all等下创建子集prio_high
prio_high
答案 1 :(得分:5)
我还有另外两种方法可以考虑这样做(虽然不如cset那么优雅,但似乎没有Redhat的支持程度):
1)任务集包括PID 1在内的所有内容 - 简单易行(但是,alledgly - 我自己从未见过任何问题 - 可能会导致调度程序效率低下)。下面的脚本(必须以root身份运行)在所有正在运行的进程上运行taskset,包括init(pid 1);这会将所有正在运行的进程固定到一个或多个“垃圾核心”,并且通过固定init,它将确保任何未来的进程也在“垃圾核心”列表中启动:
#!/bin/bash
if [[ -z $1 ]]; then
printf "Usage: %s '<csv list of cores to set as junk in double quotes>'", $0
exit -1;
fi
for i in `ps -eLfad |awk '{ print $4 } '|grep -v PID | xargs echo `; do
taskset -pc $1 $i;
done
2)使用isolcpus内核参数(这里是https://www.kernel.org/doc/Documentation/kernel-parameters.txt的文档):
isolcpus= [KNL,SMP] Isolate CPUs from the general scheduler.
Format:
<cpu number>,...,<cpu number>
or
<cpu number>-<cpu number>
(must be a positive range in ascending order)
or a mixture
<cpu number>,...,<cpu number>-<cpu number>
This option can be used to specify one or more CPUs
to isolate from the general SMP balancing and scheduling
algorithms. You can move a process onto or off an
"isolated" CPU via the CPU affinity syscalls or cpuset.
<cpu number> begins at 0 and the maximum value is
"number of CPUs in system - 1".
This option is the preferred way to isolate CPUs. The
alternative -- manually setting the CPU mask of all
tasks in the system -- can cause problems and
suboptimal load balancer performance.
我已经将这两个加上cset机制用于几个项目(顺便说一句,请原谅公然的自我推销:-)),我刚刚为一个名为Pontus Vision ThreadManager的工具申请了专利任何给定x86平台对任何给定软件工作负载的最佳锁定策略;在客户站点测试之后,我得到了非常好的结果(峰值延迟减少了270%),因此非常值得进行固定和CPU隔离。
答案 2 :(得分:0)
这是使用cgroups的老式方法。我有一台Fedora 28机器,而RedHat / Fedora希望您使用systemd-run
,但是我无法在其中找到此功能。如果有人愿意启发我,我很想知道如何使用systemd-run
来做到这一点。
假设我要从调度中排除我的第四个CPU(CPU 0-3),并将所有现有进程移至CPU 0-2。然后,我想单独将一个进程放在CPU 3上。
sudo su -
cgcreate -g cpuset:not_cpu_3
echo 0-2 > /sys/fs/cgroup/cpuset/not_cpu_3/cpuset.cpus
# This "0" is the memory node. See https://utcc.utoronto.ca/~cks/space/blog/linux/NUMAMemoryInfo
# for more information *
echo 0 > /sys/fs/cgroup/cpuset/not_cpu_3/cpuset.mems
/proc/zoneinfo
和/sys/devices/system/node
的层次结构。留给读者正确的节点信息。现在我们有了cgroup,我们需要创建隔离的CPU 3 cgroup:
cgcreate -g cpuset:cpu_3
echo 3 > /sys/fs/cgroup/cpuset/cpu_3/cpuset.cpus
# Again, the memory node(s) you want to specify.
echo 0 > /sys/fs/cgroup/cpuset/cpu_3/cpuset.mems
将所有进程/线程放在not_cpu_3
cgroup上:
for pid in $(ps -eLo pid) ; do cgclassify -g cpuset:not_cpu_3 $pid; done
评论:
ps -eL k psr o psr,pid,tid,args | sort | cut -c -80
注意!当前处于睡眠状态的进程将不会移动。必须将它们唤醒,以便调度程序将它们放置在其他CPU上。要看到这一点,请在上面的列表中选择您喜欢的睡眠过程-例如Web浏览器,您认为该过程应该在CPU 0-2上,但仍在3上。使用上面列表中的线程ID,执行: / p>
kill -CONT <thread_id>
示例
kill -CONT 9812
重新运行ps命令,并注意它已移至另一个CPU。
请注意!某些内核线程无法并且不会移动!例如,您可能会注意到,每个CPU都有一个内核线程[kthreadd]
。将进程分配给cgroup仅适用于用户空间进程,不适用于内核线程。这就是多任务世界中的生活。
现在将一个进程及其所有子进程移至控制组cpu_3:
pid=12566 # for example
cgclassify -g cpuset:cpu_3 $pid
taskset -c -p 3 $pid
同样,如果$pid
处于睡眠状态,则需要唤醒它以使CPU移动真正发生。
要撤消所有这些操作,只需删除您创建的cgroup。每个人都会回到根cgroup中:
cgdelete -r cpuset:cpu_3
cgdelete -r cpuset:not_cpu_3
无需重启。
(对不起,我不明白原始海报中的第三个问题。我无法对此发表评论。)
答案 3 :(得分:0)
如果您使用的是rhel实例,则可以为此使用Tuna(也可以用于其他Linux发行版,但不确定如何)。可以从yum命令轻松安装。金枪鱼可用于隔离CPU核心,它可以将在该特定CPU中运行的进程动态移至相邻的CPU。隔离CPU核心的命令如下,
company_good_id
您可以使用# tuna --cpus=CPU-LIST --isolate
来查看金枪鱼如何实时隔离CPU核心。