stress(1) を使うと、Unix 系 OS で動作しているホストの CPU やメモリ、ディスクに簡単に負荷をかけられる。 今回は使い方や動作などを一通り見ていく。
使った環境は次のとおり。
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04 LTS Release: 22.04 Codename: jammy $ uname -srm Linux 5.15.0-40-generic aarch64 $ stress --version stress 1.0.5 $ dstat --version | head -n 1 pcp-dstat 5.3.6 $ taskset --version taskset from util-linux 2.37.2
もくじ
下準備
あらかじめ必要なパッケージをインストールしておく。
肝心の stress(1) は stress
パッケージで入る。
$ sudo apt-get -y install stress pcp util-linux
CPU に負荷をかける
まずは CPU に負荷をかける方法から。 今回は環境として CPU が 4 コアの仮想マシンを使っている。
$ cat /proc/cpuinfo | grep processor | wc -l 4
CPU に負荷をかけるときは stress
コマンドと共に -c
オプションを使う。
引数として、負荷をかけるのに使うワーカーのプロセス数を指定する。
$ stress -c 1
ワーカーはランダムな数値に対する平方根を無限ループで計算することで CPU に負荷をかける。 基本的にプロセス数 = 負荷をかけるコア数と考えれば良い。
dstat(1) で確認すると、-c 1
なら 4 コア環境において user 時間を 25% (1/4) 消費している。
$ dstat -c ----total-usage---- usr sys idl wai stl 25 0 75 0 0 25 0 75 0 0 25 0 75 0 0 ...
試しにワーカー数を 2 に増やしてみよう。
$ stress -c 2
すると、次のように消費する user 時間が 50% (2/4) に増えた。
$ dstat -c ----total-usage---- usr sys idl wai stl 50 0 50 0 0 51 0 50 0 0 50 0 50 0 0 ...
特定の CPU コアを指定して負荷をかけたい場合は taskset(1) と組み合わせるのが良い。 以下では stress(1) が 0 番コアで動作するように割り当てている。
$ taskset -c 0 stress -c 1
dstat(1) で確認すると、たしかに cpu0 で user 時間を消費していることが確認できる。
$ dstat -c -C 0,1,2,3 -----cpu0-usage----------cpu1-usage----------cpu2-usage----------cpu3-usage---- usr sys idl wai stl:usr sys idl wai stl:usr sys idl wai stl:usr sys idl wai stl 100 0 0 0 0: 0 0 100 0 0: 0 0 100 0 0: 0 0 101 0 0 100 0 0 0 0: 0 0 99 0 0: 0 0 100 0 0: 0 0 100 0 0 100 0 0 0 0: 0 0 100 0 0: 0 0 100 0 0: 0 0 100 0 0 ...
次は試しに 1 番と 2 番コアに割り当ててみよう。
$ taskset -c 1,2 stress -c 2
次のとおり、ちゃんと cpu1 と cpu2 の user 時間を消費している。
$ dstat -c -C 0,1,2,3 -----cpu0-usage----------cpu1-usage----------cpu2-usage----------cpu3-usage---- usr sys idl wai stl:usr sys idl wai stl:usr sys idl wai stl:usr sys idl wai stl 0 0 100 0 0:100 0 0 0 0:100 0 0 0 0: 0 0 101 0 0 0 0 100 0 0:100 0 0 0 0:100 0 0 0 0: 0 0 100 0 0 0 0 100 0 0:100 0 0 0 0:100 0 0 0 0: 0 0 100 0 0
メモリに負荷をかける
続いてはメモリに負荷をかけてみよう。
メモリに負荷をかけるときはオプションとして -m
を指定する。
引数は CPU のときと同じで実際の処理をするワーカープロセスの数になる。
--vm-bytes
は確保するメモリのサイズを指定している。
また、--vm-hang 0
はメモリを確保したまま処理を止めるオプションになっている。
-v
はワーカープロセスの動作を詳細にログに残すため。
$ stress -m 1 --vm-bytes 512M --vm-hang 0 -v stress: info: [23977] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd stress: dbug: [23977] using backoff sleep of 3000us stress: dbug: [23977] --> hogvm worker 1 [23978] forked stress: dbug: [23978] allocating 536870912 bytes ... stress: dbug: [23978] touching bytes in strides of 4096 bytes ... stress: dbug: [23978] sleeping forever with allocated memory
上記のログを見ると、ワーカープロセスが 512MB のメモリを確保した上で、そのメモリにアクセスしている。
ps(1) で確認すると、ワーカーのプロセスの VSZ (仮想メモリ) と RSS (物理メモリ) が増加している。
$ ps -C stress -o command,pid,vsz,rss COMMAND PID VSZ RSS stress -m 1 --vm-bytes 512M 23977 3704 1316 stress -m 1 --vm-bytes 512M 23978 527996 525276 $ pstree -p $(pgrep stress | head -n 1) stress(23977)───stress(23978)
--vm-hang 0
を指定しないと、どのような挙動になるだろうか。
$ stress -m 1 --vm-bytes 512M
プロセスのメモリを見ると、VSZ はそのままで RSS が定期的に増えたり減ったりするはず。
$ watch -n 1 ps -C stress -o command,pid,vsz,rss
なぜこのような挙動になるかは -v
オプションをつけて実行すると理解できる。
一瞬でログが流れるので head(1) を使って先頭だけ確認する。
$ stress -m 1 --vm-bytes 512M -v | head -n 15 stress: info: [23993] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd stress: dbug: [23993] using backoff sleep of 3000us stress: dbug: [23993] --> hogvm worker 1 [23995] forked stress: dbug: [23995] allocating 536870912 bytes ... stress: dbug: [23995] touching bytes in strides of 4096 bytes ... stress: dbug: [23995] freed 536870912 bytes stress: dbug: [23995] allocating 536870912 bytes ... stress: dbug: [23995] touching bytes in strides of 4096 bytes ... stress: dbug: [23995] freed 536870912 bytes stress: dbug: [23995] allocating 536870912 bytes ... stress: dbug: [23995] touching bytes in strides of 4096 bytes ... stress: dbug: [23995] freed 536870912 bytes stress: dbug: [23995] allocating 536870912 bytes ... stress: dbug: [23995] touching bytes in strides of 4096 bytes ... stress: dbug: [23995] freed 536870912 bytes stress: FAIL: [23993] (416) <-- worker 23995 got signal 13 stress: WARN: [23993] (418) now reaping child worker processes stress: FAIL: [23993] (452) failed run completed in 1s
上記から、メモリを確保してアクセスしたらすぐに開放するのをずっと繰り返すことが分かる。
ちなみに --vm-hang
はメモリをアクセスした後に開放するまでの時間を制御できる。
たとえば引数に 2
を指定すると、アクセス後に 2 秒待ってから開放する挙動になる。
$ stress -m 1 --vm-bytes 512M --vm-hang 2 -v | head -n 15 stress: info: [24077] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd stress: dbug: [24077] using backoff sleep of 3000us stress: dbug: [24077] --> hogvm worker 1 [24079] forked stress: dbug: [24079] allocating 536870912 bytes ... stress: dbug: [24079] touching bytes in strides of 4096 bytes ... stress: dbug: [24079] sleeping for 2s with allocated memory stress: dbug: [24079] freed 536870912 bytes stress: dbug: [24079] allocating 536870912 bytes ... stress: dbug: [24079] touching bytes in strides of 4096 bytes ... stress: dbug: [24079] sleeping for 2s with allocated memory stress: dbug: [24079] freed 536870912 bytes stress: dbug: [24079] allocating 536870912 bytes ... stress: dbug: [24079] touching bytes in strides of 4096 bytes ... stress: dbug: [24079] sleeping for 2s with allocated memory stress: dbug: [24079] freed 536870912 bytes stress: FAIL: [24077] (416) <-- worker 24079 got signal 13 stress: WARN: [24077] (418) now reaping child worker processes stress: FAIL: [24077] (452) failed run completed in 7s
なお、-m
の引数を増やしたときは、各ワーカープロセスごとにメモリの確保・アクセス・開放をすることになる。
$ stress -m 2 --vm-bytes 512M $ pstree -p $(pgrep stress | head -n 1) stress(21021)─┬─stress(21022) └─stress(21023) $ ps -C stress -o command,pid,vsz,rss COMMAND PID VSZ RSS stress -m 2 --vm-bytes 512M 21021 3704 1376 stress -m 2 --vm-bytes 512M 21022 527996 273084 stress -m 2 --vm-bytes 512M 21023 527996 383700
また、--vm-keep
を指定すると、メモリを開放しなくなる。
つまり、一度確保したメモリにアクセスすることだけを何度も繰り返す。
たとえば --vm-hang
と組み合わせると、インターバルを入れながらメモリにアクセスする。
$ stress -m 1 --vm-keep --vm-hang 2 -v | head -n 10 stress: info: [27420] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd stress: dbug: [27420] using backoff sleep of 3000us stress: dbug: [27420] --> hogvm worker 1 [27422] forked stress: dbug: [27422] allocating 268435456 bytes ... stress: dbug: [27422] touching bytes in strides of 4096 bytes ... stress: dbug: [27422] sleeping for 2s with allocated memory stress: dbug: [27422] touching bytes in strides of 4096 bytes ... stress: dbug: [27422] sleeping for 2s with allocated memory stress: dbug: [27422] touching bytes in strides of 4096 bytes ... stress: dbug: [27422] sleeping for 2s with allocated memory stress: FAIL: [27420] (416) <-- worker 27422 got signal 13 stress: WARN: [27420] (418) now reaping child worker processes stress: FAIL: [27420] (452) failed run completed in 6s
ちなみに、上記で「メモリにアクセスする」というのは char の 'Z'
を各アドレスに書き込むことを指している。
ディスクに負荷をかける
続いてはディスクに負荷をかける方法について。
ディスクに負荷をかけるときは -d
オプションを指定する。
引数はこれまでと同じでワーカープロセスの数になる。
$ stress -d 1
dstat(1) で確認すると、次のように確かにディスクに書き込みが生じている。
$ dstat -d -D sda,total --dsk/sda----dsk/total- read writ: read writ 0 822M: 0 822M 0 1710M: 0 1710M 0 651M: 0 651M ...
stress(1) の実装としてはカレントワーキングディレクトリ以下に mkstemp(3) を使ってファイルを作ることで実現している。
そのため負荷をかけたい特定のディスクがあるならマウントしたパスで実行する必要がある。
上記はカレントワーキングディレクトリが sda
デバイス上にあることを示す。
挙動は -v
オプションをつけて実行すると分かりやすい。
実行していても作業中のファイルが見えないのは、ファイルを開いた直後に unlink(2) しているため。
$ stress -d 1 -v stress: info: [22636] dispatching hogs: 0 cpu, 0 io, 0 vm, 1 hdd stress: dbug: [22636] using backoff sleep of 3000us stress: dbug: [22636] --> hoghdd worker 1 [22637] forked stress: dbug: [22637] seeding 1048575 byte buffer with random data stress: dbug: [22637] opened ./stress.H9UjIh for writing 1073741824 bytes stress: dbug: [22637] unlinking ./stress.H9UjIh stress: dbug: [22637] fast writing to ./stress.H9UjIh stress: dbug: [22637] slow writing to ./stress.H9UjIh stress: dbug: [22637] closing ./stress.H9UjIh after 1073741824 bytes stress: dbug: [22637] opened ./stress.0X3Feo for writing 1073741824 bytes stress: dbug: [22637] unlinking ./stress.0X3Feo stress: dbug: [22637] fast writing to ./stress.0X3Feo stress: dbug: [22637] slow writing to ./stress.0X3Feo stress: dbug: [22637] closing ./stress.0X3Feo after 1073741824 bytes stress: dbug: [22637] opened ./stress.ErRA1c for writing 1073741824 bytes stress: dbug: [22637] unlinking ./stress.ErRA1c stress: dbug: [22637] fast writing to ./stress.ErRA1c stress: dbug: [22637] slow writing to ./stress.ErRA1c stress: dbug: [22637] closing ./stress.ErRA1c after 1073741824 bytes
作られるファイルサイズはデフォルトで 1GB になっている。
ファイルサイズは --hdd-bytes
オプションで指定できる。
指定したファイルサイズが小さいと、短いスパンで作業用のファイルが作られたり消えたりする。
$ stress -d 1 --hdd-bytes 1M -v stress: info: [22663] dispatching hogs: 0 cpu, 0 io, 0 vm, 1 hdd stress: dbug: [22663] using backoff sleep of 3000us stress: dbug: [22663] --> hoghdd worker 1 [22665] forked stress: dbug: [22665] seeding 1048575 byte buffer with random data stress: dbug: [22665] opened ./stress.r7I03v for writing 1048576 bytes stress: dbug: [22665] unlinking ./stress.r7I03v stress: dbug: [22665] fast writing to ./stress.r7I03v stress: dbug: [22665] slow writing to ./stress.r7I03v stress: dbug: [22665] closing ./stress.r7I03v after 1048576 bytes stress: dbug: [22665] opened ./stress.lN80Ka for writing 1048576 bytes stress: dbug: [22665] unlinking ./stress.lN80Ka stress: dbug: [22665] fast writing to ./stress.lN80Ka stress: dbug: [22665] slow writing to ./stress.lN80Ka stress: dbug: [22665] closing ./stress.lN80Ka after 1048576 bytes ...
また、これは直接的なディスクへの負荷ではないけど、関連するオプションとして -i
がある。
これは sync(2) を発行しまくるワーカープロセスを作るためのオプション。
sync(2) は、キャッシュされた書き込みを永続ストレージに同期するためのシステムコール。
$ stress -i 1 stress: info: [22656] dispatching hogs: 0 cpu, 1 io, 0 vm, 0 hdd
上記を実行すると無限ループで sync(2) を実行しまくるワーカープロセスが生成される。
ユースケースとしては -d
オプションと組み合わせて使う感じなのかな。
まとめ
今回は stress(1) を使って Unix 系 OS で動作するホストに様々な負荷をかける方法について見てみた。