【Linux】cgroupsを用いてプログラムの実行時間とリソース割当の関係を把握する

# はじめに
cgroupsは、**cpu時間やメモリなどのリソース(これらの単一のリソースはサブシステムと呼ばれる)をユーザ指定のプロセスに割り当てることができるLinuxの仕組み**です。身近なところでは、Dockerコンテナへのリソース割当においてcgroupsが利用されているようです。また、cgroupsは、プログラムの実行時間とリソース割当の関係を見積もる際にも役立ちます。
本記事は、カウントアップのプログラムを題材にして、**cgroupsによってcpu時間とメモリを制限されたプログラムの実行時間を計測したもの**です。

# 対象読者
・プログラムの実行時間とリソースの関係を把握したい方

# 前提条件
OS : Ubuntu 20.04
Kernel : 5.15.0-52-generic

# 1. 準備
## 1.1 cgroup-toolsのインストール
cgroup-toolsをインストールします。
```
# sudo apt install cgroup-tools
```
## 1.2 リソース割当に関するパラメータ設定と反映
/etc/cgconfig.confにリソース割当に関するパラメータを設定していきます。
cpu.cfs_period_usはcpu時間の割当周期を指定するパラメータです。
cpu.cfs_quota_usはプロセスが使用するコア全体で占有するcpu時間を指定するパラメータです。注意事項としては、例えば、プロセスが2コアを使用する場合、単一コアの使用時間は、cpu.cfs_quota_usとして設定した値の半分になります。従って、下記の設定値の場合、単一コアのcpu利用時間は、125000[us]となります。
memory.limit_in_bytesは利用可能なメモリの最大値を指定するパラメータです。
```
# vi /etc/cgconfig.conf 
group  mycgr1{ 
	cpu {
		cpu.cfs_quota_us  =  250000;
		cpu.cfs_period_us = 1000000;
	}
    memory {
        memory.limit_in_bytes = 1M;
    }
	cpuacct {
	}
}
```
下記のコマンドでパラメータを反映させます。
```
# cgconfigparser -l /etc/cgconfig.conf 
```
## 1.3 動作確認
先ずは、cgroupsを使わない場合のストレステストを実施します。
```shell 1
# sudo  stress -c 2
stress: info: [3665] dispatching hogs: 2 cpu, 0 io, 0 vm, 0 hdd
```
2コアともに、cpu使用率が100%近くになっています。
```shell 2
# top
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND  
   3667 root      20   0    3864     96      0 R 100.0   0.0   0:21.28 stress   
   3666 root      20   0    3864     96      0 R  99.7   0.0   0:21.24 stress 
```
次に、cgroupsを使った場合のストレステストを実施します。
```shell 1
# sudo cgexec -g cpu,memory:mycgr1 stress -c 2
stress: info: [3673] dispatching hogs: 2 cpu, 0 io, 0 vm, 0 hdd
```
2コアを利用しているため、単一コアのcpu使用率は12.5%程度になっています。設定したパラメータから期待されるcpu使用率と一致しているため、正常に動作していると言えます。

```shell 2
# top
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND  
   3675 root      20   0    3864    100      0 R  12.6   0.0   0:02.20 stress   
   3674 root      20   0    3864    100      0 R  12.3   0.0   0:02.20 stress
```
# 2. 試験
cgroupsにてcpu利用率やメモリを制限しながら、カウントアップするプログラムの実行時間を計測していきます。

## 2.1 カウントアップ・プログラムの作成
100万までカウントアップするプログラムを作成しました。
``` test.sh
#!/bin/bash

a=$(date "+%s") 
k=0
for i in `seq 1000000 -1 1`
do
    k=$(($k+1))
done
b=$(date "+%s")
echo "Cout Value:"$k
echo "Elapsed Time:"$((b-a))"[sec]"
```
test.shに実行権限を与えて実行すると、カウンターの値と実行時間が表示されます。尚、このときのcpu使用率は100%となり、実行時間は２秒でした。
```
# ./test.sh 
Cout Value:1000000
Elapsed Time:2[sec]
```
## 2.2 cgroupsによるリソース制限とプログラム実行時間の関係
### 2.2.1 cpu利用率を25%に制限した場合
cpu利用率25％にした場合のプログラム実行時間を計測しました。
下記コマンドでプログラムを実行した後のプログラム実行時間は8秒となっていました。cpu利用率が100%の場合は2秒であったため、実行時間が4倍になっているのは妥当と言えます。
```
# sudo cgexec -g cpu:mycgr1 ./test.sh 
Cout Value:1000000
Elapsed Time:8[sec]
```
### 2.2.2 メモリを1MBに制限した場合
メモリを1MB割り当てた場合のプログラム実行時間を計測しました。
下記コマンドで実行した後のプログラム実行時間は15秒となっていました。メモリが少なすぎるために、プログラムの速度が低下してしまったと考えられます。
```
# sudo cgexec -g memory:mycgr1 ./test.sh 
Cout Value:1000000
Elapsed Time:15[sec]
```
### 2.2.3 cpu利用率を25%、メモリを1MBに制限した場合
cpu利用率を25％、メモリを1MBにした場合のプログラム実行時間を計測しました。
下記コマンドで実行した後のプログラム実行時間は41秒となっており、cpuとメモリのリソースが共に制限されているために実行時間がより長くなってしまったと考えられます。
```
# sudo cgexec -g cpu,memory:mycgr1 ./test.sh 
Cout Value:1000000
Elapsed Time:41[sec]
```

# 3. まとめ
本記事では、cgroupsを用いることで、プログラムの実行時間とリソース割当の関係を把握できることを示しました。

# 参考文献
https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/6/html/resource_management_guide/ch-using_control_groups

https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/6/html/resource_management_guide/sec-cpu

https://christina04.hatenablog.com/entry/2020/02/24/170724

https://qiita.com/_mmasaki/items/639193ebb68ff5c94cbc

https://github.com/matsumotory/libcgroup

https://docs.oracle.com/cd/E86432_01/bigData.Doc/install_onPrem/src/tins_prereq_index_cgroups.html

https://qiita.com/legitwhiz/items/72ead813f5be784534e5

https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/cgroups.html

# おまけ
/sys/fs/cgroup/の配下には、収集した統計情報などが格納されています。
```
# ls /sys/fs/cgroup/
blkio    cpu,cpuacct  freezer  misc              net_prio    rdma
cpu      cpuset       hugetlb  net_cls           perf_event  systemd
cpuacct  devices      memory   net_cls,net_prio  pids        unified
```