cgroups v2を使って1コアCPU環境でテストする
Live With Linux::technique
- TOP
- Articles
- Live With Linux
- cgroups v2を使って1コアCPU環境でテストする
序
動機としては、動画エンコーディングの際になぜかCPUの利用率を無視してどのコーデックが速いの遅いのという話をされがちなので、シングルコアのCPU上でテストしたかったというもの。
これを実現するために、おおまかにはcgroupsを使う方法とtasksetを使う方法がある。 このうち、cgroups v2を使う方法は調べてもあまり出てこないので、ここにまとめることにした。
そもそもcgroupsとは
cgroupsは、名前のまんまだが、リソースや名前空間を制御するコントロールグループを取り扱うものである。
プロセスをコントロールグループに所属させることで、namespaceなどの機能を適用することができる。
リソース制御の機能そのものではなく、それらをまとめるもののこと。
cgroups v2を使う
cgroupsの使い方は色々あるけれど、cgroups2fsを使うのがおそらく最も簡単で汎用性がある。
ここではcgroups2
が/sys/fs/cgroup
にマウントされている前提で話を進める。
cgroups2
ファイルシステムにディレクトリを作ると、新たなコントロールグループが作成される。
# mkdir /sys/fs/cgroup/sandboxxx
コントロールグループディレクトリ以下には自動的にコントローラが配置される。
cgroup.controllers
cgroup.events
cgroup.freeze
cgroup.kill
cgroup.max.depth
cgroup.max.descendants
cgroup.pressure
cgroup.procs
cgroup.stat
cgroup.threads
cgroup.type
cgroup.subtree_control
cpu.idle
cpu.max
cpu.max.burst
cpu.pressure
cpu.stat
cpu.uclamp.max
cpu.uclamp.min
cpu.weight
cpu.weight.nice
io.pressure
irq.pressure
memory.current
memory.events
memory.events.local
memory.high
memory.low
memory.max
memory.min
memory.oom.group
memory.peak
memory.pressure
memory.reclaim
memory.stat
memory.swap.current
memory.swap.events
memory.swap.high
memory.swap.max
memory.zswap.current
memory.zswap.max
memory.numa_stat
pids.current
pids.events
pids.max
pids.peak
CPU数の制御にはcpuset
コントローラを使用するが、この場合は有効になっていないことが見て取れる。
そこで、まずcgroups v2上でcpusetコントローラを有効化する。
# echo "+cpuset" >> /sys/fs/cgroup/cgroup.subtree_control
続いて、sandboxxx
コントロールグループに対してcpuset
コントローラを有効にする。
# echo "+cpuset" > /sys/fs/cgroup/sandboxxx/cgroup.subtree_control
cpuset
コントローラを使い、sandboxxx
コントロールグループにはプロセッサ0
だけを割り当てる。
# echo 0 > /sys/fs/cgroup/sandboxxx/cpuset.cpus
そして、cgroup.procs
コントローラにこのコントロールグループを適用するプロセスを記入する。
ここではpid 10000
のプロセスに適用した。
echo "10000" > /sys/fs/cgroup/sandboxxx/cgroup.procs
子プロセスに対しても適用されるので、シェルプロセスを指定すると扱いやすい。
tasksetを使う
taskset
を使うとプロセスは指定されたCPUを使うようになる。
結果はcgroupsと似たようなものではあるが、cgroupsの場合はプロセスから見て利用できるCPUそのものが限定されているのに対し、tasksetを使って制限した場合はプロセスがどんなにマルチスレッドで動作しても、Linuxカーネルは指定されたCPUの時間しか割り当てない。
taskset
はユーザーレベルで実行可能なのもメリット。次のようにしてZshログインシェルをCPU0のみを使用する状態で起動する。
% taskset -c 0 zsh -l
cpulimitを使う
cpulimit
を使うとCPU時間をパーセンテージで制限することができる。
これはLinuxの基本機能ではなく、cpulimitというソフトウェアがある。
% cpulimit -l 100 -p 10000
とすると、pid
10000
のプロセスのCPU利用量を100%(CPUコア1つ分)に制限する。
この方法はCPU時間割当があまり安定せず、CPUリソースの制限された環境をテストするのには向かない印象を受けた。
cgroups v2でCPUを時間量的に制限する
cpu
コントローラのcpu.max
を使えばcgroups
v2を用いてCPU帯域を制限することができる。
書式は
$MAX $PERIOD
であり、$PERIOD
期間中$MAX
だけCPU時間を使うことを許可する。デフォルトはmax 10000
.
注意してほしいのは、
10000 10000
は無制限を意味しないということだ。
この場合、CPUを100%、つまり1コア分相当だけ使うことを許可することになる。16コアであれば最大値は160000
になる。
先程のsandboxxx
にCPU利用率200%を許可する場合、
# echo "20000 10000" > /sys/fs/cgroup/sandboxxx/cpu.max
となる。
このようにすれば、擬似的にCPUリソースの乏しい環境をテストできる。
で、テストの結果は?
Street Fighter 6のプレイ動画を使ってエンコードした結果。動きの激しさが変化するので、だいたい1:00あたりの速度を計測した。
1コアだけを割り当てているので、CPU時間に対する効率と考えて良い。
速度に関連するファクターは与えず、画質調整によってサイズだけをある程度揃えるようにした。
ただし、libaom-av1に関しては極端に時間がかかるため、サイズを揃える作業はせず、単に-crf 34
を与えている。
- 23fps libx264 (H.264)
- 10fps libx265 (HEVC)
- 10fps libvpx (VP8)
- 5.5fps libvpx-vp9 (VP9)
- <0.1fps libaom-av1 (AV1)
- 17fps svtav1 (AV1)
あれ、SVT-AV1ってこんなに速かったっけ?
Netflixが開発するSVT-AV1コーデックだが、恐ろしいほど遅いlibaom-av1と比べてかなり高速ということで話題になった。 ただ、初期に私が試した限りでは並列化が得意なだけで、CPU時間比だとあんまり変わらないという印象だった。
ただ、CRF値がlibvpx-vp9やlibaom-av1とはかなり異なっているため、サイズを小さくしようとすると調整が必要。 また、libvpx-vp9やlibaom-av1にあるConstrained Qualityがないため、調整が難しいほか、libvpx-vp9と比べてQuality/Bitrateの比が悪い。つまり、libvpx-vp9によるVP9動画と同等の視覚品質を得ようとすると、svtav1のほうがファイルサイズが大きくなる。 特にlibvpx-vp9はゲーム動画のような、画面に部分的に動きがあったりなかったりするような動画をかなり小さく圧縮するため、ゲームキャプチャのエンコードとしてはやや微妙かもしれない。
ただ、libx265比でも速く、並列化も(libx265ほどではないにせよ)効くので、libvpx-vp9で設定を詰めるほどではないが、実時間で速くエンコードしたい場合などには使えそう。
アーカイブに使うにはサイズ、品質ともにやや厳しい。 libvpx-vp9と比べると3〜5倍程度のビットを割り当てないとうまくいかない印象だった。
だいたい10Mbps程度でcvbrにすると悪くないので、ハードウェアエンコーディングに近い印象かもしれない。 ただ、性質がハードウェアエンコーディングに近すぎるため、ハードウェアエンコーディングできる環境では選択理由があまりない感じだ。