sched_setaffinity
を使用してスレッドをCPUに固定し、パフォーマンスを向上させることができます(状況によっては)
Linuxのmanページから:
プロセスを単一のCPUで実行するように制限すると、プロセスが1つのCPUで実行を停止し、別のCPUで実行を再開したときに発生するキャッシュの無効化によって引き起こされるパフォーマンスコストも回避されます。
さらに、よりリアルタイムの応答が必要な場合は、そのスレッドのスケジューラポリシーをSCHED_FIFO
に変更し、優先度をいくつかの高い値(最大sched_get_priority_max
)に上げることができます。準備ができると、質問は常にそのCPUで実行中の他のスレッドを横取りする必要があります。
ただし、この時点で、リアルタイムスレッドがプリエンプトしたばかりのCPUで実行されているスレッドが、リアルタイムスレッドのレベル1キャッシュエントリの多くを追い出した可能性があります。
私の質問は次のとおりです。
答えは cpusets を使用することです。 python cpusetユーティリティ を使用すると、設定が簡単になります。
基本的な概念
3 cpusets
root
:すべての構成に存在し、すべてのCPUを含む(unshielded)system
:システムタスクに使用されるCPUが含まれます-実行する必要があるが「重要」ではない(unshielded)user
:「重要な」タスクに使用されるCPUが含まれます-「リアルタイム」モードで実行するタスク(shielded)shield
コマンドは、これら3つのcpusetsを管理します。
セットアップ中に、すべての移動可能なタスクをシールドされていないcpuset(system
)に移動し、ティアダウン中にすべての移動可能なタスクをroot
cpusetに移動します。セットアップ後、サブコマンドを使用すると、タスクをshield(user
)cpusetに移動できます。さらに、特別なタスク(カーネルスレッド)をroot
からsystem
に(したがって、 user
cpuset)。
コマンド:
まず、シールドを作成します。当然、シールドのレイアウトはマシン/タスクに依存します。たとえば、4コアの非NUMAマシンがあるとします。シールドに専用コア3コアを割り当て、重要でないタスク用の1つのコア;これは非NUMAなので、メモリノードパラメータを指定する必要はなく、カーネルスレッドをroot
cpusetで実行したままにします(つまり、すべてのCPUで)。
$ cset shield --cpu 1-3
一部のカーネルスレッド(特定のCPUにバインドされていないもの)は、system
cpusetに移動できます。 (一般に、特定のCPUにバインドされているカーネルスレッドを移動することはお勧めしません)
$ cset shield --kthread on
シールド(user
)またはシールドされていない(system
)cpusetsで実行されているものをリストしましょう:(-v
は、プロセス名をリストします)(2番目の-v
を追加して、80文字以上を表示します)
$ cset shield --shield -v
$ cset shield --unshield -v -v
シールドを止めたい場合(ティアダウン)
$ cset shield --reset
シールドでプロセスを実行しましょう('--'
に続くコマンドは、cset
ではなく、実行するコマンドに渡されます)
$ cset shield --exec mycommand -- -arg1 -arg2
シールドに移動したい実行中のプロセスが既にある場合(コンマ区切りのリストまたは範囲を渡すことで複数のプロセスを移動できることに注意してください(ギャップがある場合でも、範囲内のすべてのプロセスが移動されます))
$ cset shield --shield --pid 1234
$ cset shield --shield --pid 1234,1236
$ cset shield --shield --pid 1234,1237,1238-1240
高度な概念
cset set/proc
-これらにより、cpusetsをより細かく制御できます
セット
Cpusetsの作成、調整、名前変更、移動、破棄
コマンド
Cpu 1-3を使用してcpusetを作成し、NUMAノード1を使用して「my_cpuset1」と呼びます。
$ cset set --cpu=1-3 --mem=1 --set=my_cpuset1
「my_cpuset1」を変更して、CPU 1と3のみを使用する
$ cset set --cpu=1,3 --mem=1 --set=my_cpuset1
Cpusetを破棄する
$ cset set --destroy --set=my_cpuset1
既存のcpusetの名前を変更する
$ cset set --set=my_cpuset1 --newname=your_cpuset1
階層的なcpusetを作成する
$ cset set --cpu=3 --mem=1 --set=my_cpuset1/my_subset1
既存のcpusetsのリスト(深さ1)
$ cset set --list
既存のcpusetとその子をリストする
$ cset set --list --set=my_cpuset1
すべての既存のcpusetsをリストして下さい
$ cset set --list --recurse
Proc
スレッドとプロセスを管理する
コマンド
Cpusetで実行中のタスクを一覧表示する
$ cset proc --list --set=my_cpuset1 --verbose
Cpusetでタスクを実行する
$ cset proc --set=my_cpuset1 --exec myApp -- --arg1 --arg2
タスクを移動する
$ 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
タスクとそのすべての兄弟を移動する
$ cset proc --move --toset=my_cpuset1 --pid 1234 --threads
すべてのタスクを1つのCPUセットから別のCPUセットに移動する
$ cset proc --move --fromset=my_cpuset1 --toset=system
固定されていないカーネルスレッドをcpusetに移動する
$ cset proc --kthread --fromset=root --toset=system
カーネルスレッド(特定のcpuに固定されているスレッドを含む)をcpusetに強制的に移動します(注:これはシステムに悲惨な結果をもたらす可能性があります-何をしているのかを確認してください)
$ cset proc --kthread --fromset=root --toset=system --force
階層の例
階層的なcpusetsを使用して、優先順位付けされたグループを作成できます
system
cpusetを作成しますprio_low
cpusetを作成します(1)prio_met
cpusetを作成します(1-2)prio_high
cpusetを作成するprio_all
cpusetを4つすべてのCPU(0〜3)で作成します(これはrootと同じであることに注意してください。rootから分離することをお勧めします)上記を実現するには、prio_allを作成してから、prio_allの下にサブセットprio_highを作成します。
$ cset set --cpu=0 --set=system
$ cset set --cpu=0-3 --set=prio_all
$ cset set --cpu=1-3 --set=/prio_all/prio_high
$ cset set --cpu=1-2 --set=/prio_all/prio_high/prio_med
$ cset set --cpu=1 --set=/prio_all/prio_high/prio_med/prio_low
これを行うには、他に2つの方法が考えられます(ただし、Red Hatからの素晴らしいレベルのサポートはないようですが、csetほどエレガントではありません)。
1)PID 1を含むすべてのタスクセット-素晴らしくて簡単です(しかし、私自身は問題を見たことがないので、スケジューラーの非効率性を引き起こす可能性があります)。以下のスクリプト(rootとして実行する必要があります)は、init(pid 1)を含む実行中のすべてのプロセスでタスクセットを実行します。これにより、実行中のすべてのプロセスが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.
私はいくつかのプロジェクトでこれら2つとcsetメカニズムを使用しました(偶然にも、露骨な自己宣伝を許してください:-))、私は Pontus Vision ThreadManager というツールの特許を申請しました特定のx86プラットフォームの特定のソフトウェアワークロードへの最適なピン留め戦略を実行します。お客様のサイトでテストした後、私は本当に良い結果(ピークレイテンシの270%削減)を得たので、ピン留めとCPU分離を行う価値は十分にあります。
これは、cgroupを使用して、昔ながらの方法で行う方法です。私はFedora 28マシンを持っていて、RedHat/Fedoraがsystemd-run
を使用することを望んでいますが、そこにこの機能を見つけることができませんでした。誰かが私を啓蒙したいのであれば、systemd-run
を使用してそれを行う方法を知りたいです。
(CPU 0〜3の)4番目のCPUをスケジューリングから除外し、既存のすべてのプロセスを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に置くために、それらは起こされなければなりません。これを確認するには、上記のリストからお気に入りのスリーププロセスを選択します。CPU0〜2にあるはずのプロセス(Webブラウザなど)はまだ3のままです。上記のリストのスレッドIDを使用して、以下を実行します。
kill -CONT <thread_id>
例
kill -CONT 9812
Psコマンドを再実行し、それが別のCPUに移動されていることを確認します。
ダブルノート!一部のカーネルスレッドできないで、移動しません。たとえば、すべてのCPUにカーネルスレッド[kthreadd]
があることに注意してください。プロセスをcgroupsに割り当てることは、カーネルスレッドではなく、ユーザー空間プロセスに対して機能します。これがマルチタスクの世界での生活です。
次に、プロセスとそのすべての子を制御グループ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番目の質問は理解できません。コメントはできません。)
Rhelインスタンスを使用している場合は、これにTunaを使用できます(他のLinuxディストリビューションでも使用できる場合がありますが、それについては不明です)。 yumコマンドから簡単にインストールできます。 TunaはCPUコアを分離するために使用でき、特定のCPUで実行されているプロセスを隣接するCPUに動的に移動します。 CPUコアを分離するコマンドは次のとおりです。
# tuna --cpus=CPU-LIST --isolate
htop
を使用すると、マグロがリアルタイムでCPUコアを分離する方法を確認できます。