2022-05-10

Linux Mint で zram を使ってみた

zram は Linux の仮想メモリーのスワップを圧縮 RAM ディスクとして提供する機能です。

スワップアウトが発生すると通常は HDD なり SDD に用意されているスワップファイルやスワップ領域へのディスク I/O が発生しますが、この I/O 先がメモリーとなるため処理速度が速くなるというわけです。また、スワップアウト対象のデータを圧縮してから書き出すため容量も節約できるという機能です。(その分、圧縮のために少し CPU 負荷が増加する)

さて、さっそく zram のインストールです。非常に簡単です。

apt install zram-config

sudo reboot

PC の再起動が終わるともう zram によるスワップが有効になっています。swapon コマンドで結果を確認してみましょう。

$ swapon --show
NAME       TYPE        SIZE USED PRIO
/swapfile  file        1.9G   0B   -2
/dev/zram0 partition 995.4M   0B    5
/dev/zram1 partition 995.4M   0B    5
/dev/zram2 partition 995.4M   0B    5
/dev/zram3 partition 995.4M   0B    5
/dev/zram4 partition 995.4M   0B    5
/dev/zram5 partition 995.4M   0B    5
/dev/zram6 partition 995.4M   0B    5
/dev/zram7 partition 995.4M   0B    5

ワタシの環境では /swapfile がもともと用意してあったスワップです。zram0 〜 zram7 が zram-config によって追加されたスワップ領域です。

なぜ突然に 8 個も追加したの?と思われるかもしれませんが、これは zram-config の仕様です。

搭載している CPU の論理コア数と同じ個数を用意するようになっています。さらにスワップ領域のサイズも搭載している物理メモリー容量の半分になるように確保されます。物理メモリー容量が 16GB なので、半分の 8GB を 8 個で確保できるように、ということでおおよそ 1GB ずつの領域に設定されています。(ちょっと多いかな)

ちなみに zramctl というコマンドでもう少し詳細を確認できます。

$ zramctl
NAME       ALGORITHM DISKSIZE DATA COMPR TOTAL STREAMS MOUNTPOINT
/dev/zram7 lzo-rle     995.4M   4K   74B   12K       8 [SWAP]
/dev/zram6 lzo-rle     995.4M   4K   74B   12K       8 [SWAP]
/dev/zram5 lzo-rle     995.4M   4K   74B   12K       8 [SWAP]
/dev/zram4 lzo-rle     995.4M   4K   74B   12K       8 [SWAP]
/dev/zram3 lzo-rle     995.4M   4K   74B   12K       8 [SWAP]
/dev/zram2 lzo-rle     995.4M   4K   74B   12K       8 [SWAP]
/dev/zram1 lzo-rle     995.4M   4K   74B   12K       8 [SWAP]
/dev/zram0 lzo-rle     995.4M   4K   74B   12K       8 [SWAP]

初期状態では圧縮アルゴリズムに LZO-RLE が設定されていて、これを変更することが可能です。

cat コマンドで選択可能な圧縮アルゴリズムと現在適用されている圧縮アルゴリズムを確認できます。Zstandard なども選択できるみたいです。
(これは初期値を変更したあとの結果です)

$ cat /sys/block/zram0/comp_algorithm 
lzo lzo-rle [lz4] lz4hc 842 zstd

今まで 2GB のスワップファイルを 1 本で運用してきた身には 1GB が 8 本というのは多すぎなので、もう少し減らしたいと思い、設定方法を探りました。ヒントはインストール時のログメッセージにありました。

$ apt install zram-config
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
以下のパッケージが新たにインストールされます:
  zram-config
アップグレード: 0 個、新規インストール: 1 個、削除: 0 個、保留: 0 個。
4,312 B のアーカイブを取得する必要があります。
この操作後に追加で 55.3 kB のディスク容量が消費されます。
取得:1 http://ftp.tsukuba.wide.ad.jp/Linux/ubuntu focal/universe amd64 zram-config all 0.5 [4,312 B]
4,312 B を 0秒 で取得しました (50.8 kB/s)
以前に未選択のパッケージ zram-config を選択しています。
(データベースを読み込んでいます ... 現在 295974 個のファイルとディレクトリがインストールされています。)
.../zram-config_0.5_all.deb を展開する準備をしています ...
zram-config (0.5) を展開しています...
zram-config (0.5) を設定しています ...
Created symlink /etc/systemd/system/multi-user.target.wants/zram-config.service → /lib/systemd/system/zram-config.service.

なんか /lib/systemd/system/zram-config.service というものが作成されたようです。(Linux Mint 20.3 の場合)

まずはこの中身を確認してみましょう。

$ cat /lib/systemd/system/zram-config.service 
[Unit]
Description=Initializes zram swaping

[Service]
ExecStart=/usr/bin/init-zram-swapping
ExecStop=/usr/bin/end-zram-swapping
Type=oneshot
RemainAfterExit=true

[Install]
WantedBy=multi-user.target

サービス開始時に /usr/bin/init-zram-swapping を使用するみたいです。こちらも中身を確認してみましょう。

$ cat /usr/bin/init-zram-swapping 
#!/bin/sh

# load dependency modules
NRDEVICES=$(grep -c ^processor /proc/cpuinfo | sed 's/^0$/1/')
if modinfo zram | grep -q ' zram_num_devices:' 2>/dev/null; then
  MODPROBE_ARGS="zram_num_devices=${NRDEVICES}"
elif modinfo zram | grep -q ' num_devices:' 2>/dev/null; then
  MODPROBE_ARGS="num_devices=${NRDEVICES}"
else
  exit 1
fi
modprobe zram $MODPROBE_ARGS

# Calculate memory to use for zram (1/2 of ram)
totalmem=`LC_ALL=C free | grep -e "^Mem:" | sed -e 's/^Mem: *//' -e 's/  *.*//'`
mem=$(((totalmem / 2 / ${NRDEVICES}) * 1024))

# initialize the devices
for i in $(seq ${NRDEVICES}); do
  DEVNUMBER=$((i - 1))
  echo $mem > /sys/block/zram${DEVNUMBER}/disksize
  mkswap /dev/zram${DEVNUMBER}
  swapon -p 5 /dev/zram${DEVNUMBER}
done

設定ファイルに設定値を用意しておくのではなく、このスクリプトで実行に CPU のコア数や物理メモリー容量を元に zram なスワップ領域を作成しているようです。

まずはスワップ領域の個数とサイズを決めないといけないのですが、どれくらいが適切なのかサッパリ見当がつきません。前と同じで 2GB を 1 個でもいいんですが、せっかくなので 1GB を 4 個に設定してみます。

また圧縮アルゴリズムも LZ4 に変えてみようと思います。参考にしたのはこちらのページの「Performance」という章です。
https://github.com/ecdye/zram-config

容量というか圧縮率よりもスピードを重視すると LZ4 が良いのでは?と思いまして。

$ sudo xed /usr/bin/init-zram-swapping 

#!/bin/sh

# load dependency modules
#NRDEVICES=$(grep -c ^processor /proc/cpuinfo | sed 's/^0$/1/')
NRDEVICES=4
if modinfo zram | grep -q ' zram_num_devices:' 2>/dev/null; then
  MODPROBE_ARGS="zram_num_devices=${NRDEVICES}"
elif modinfo zram | grep -q ' num_devices:' 2>/dev/null; then
  MODPROBE_ARGS="num_devices=${NRDEVICES}"
else
  exit 1
fi
modprobe zram $MODPROBE_ARGS

# Calculate memory to use for zram (1/2 of ram)
totalmem=`LC_ALL=C free | grep -e "^Mem:" | sed -e 's/^Mem: *//' -e 's/  *.*//'`
#mem=$(((totalmem / 2 / ${NRDEVICES}) * 1024))
mem=$((1024 * 1024 * 1024))

# initialize the devices
for i in $(seq ${NRDEVICES}); do
  DEVNUMBER=$((i - 1))
  echo lz4 > /sys/block/zram${DEVNUMBER}/comp_algorithm
  echo $mem > /sys/block/zram${DEVNUMBER}/disksize
  mkswap /dev/zram${DEVNUMBER}
  swapon -p 5 /dev/zram${DEVNUMBER}
done

変更したのは 3 点です。(うまく色付けできませんでした)
・NRDEVICES=4 と固定値を指定
・mem=$((1024 * 1024 * 1024)) とこちらも固定値を指定
・echo lz4 > /sys/block/zram${DEVNUMBER}/comp_algorithm を追加

comp_algorithm の設定は場所というか順番が大事で、disksize の設定の下に記載するとエラーになり設定できませんでした。

完成した zram のスワップは以下の通りです。

$ zramctl
NAME       ALGORITHM DISKSIZE DATA COMPR TOTAL STREAMS MOUNTPOINT
/dev/zram3 lz4             1G   4K   64B    4K       8 [SWAP]
/dev/zram2 lz4             1G   4K   64B    4K       8 [SWAP]
/dev/zram1 lz4             1G   4K   64B    4K       8 [SWAP]
/dev/zram0 lz4             1G   4K   64B    4K       8 [SWAP]

$ swapon --show
NAME       TYPE       SIZE USED PRIO
/dev/zram0 partition 1024M   0B    5
/dev/zram1 partition 1024M   0B    5
/dev/zram2 partition 1024M   0B    5
/dev/zram3 partition 1024M   0B    5

さあ、早くスワップアウトよ発生するのだ。

(その後)
Timeshift の定時バックアップが走ったあたりで待望の?スワップアウトが発生しました。swapon コマンドと zramctl コマンドでそれぞれ使用状況を確認してみます。

$ swapon --show
NAME       TYPE       SIZE USED PRIO
/dev/zram0 partition 1024M 768K    5
/dev/zram1 partition 1024M 768K    5
/dev/zram2 partition 1024M 512K    5
/dev/zram3 partition 1024M 512K    5

$ zramctl
NAME       ALGORITHM DISKSIZE  DATA  COMPR TOTAL STREAMS MOUNTPOINT
/dev/zram3 lz4             1G  516K 133.8K  368K       8 [SWAP]
/dev/zram2 lz4             1G  388K 116.6K  388K       8 [SWAP]
/dev/zram1 lz4             1G  140K  36.5K  204K       8 [SWAP]
/dev/zram0 lz4             1G  604K 134.6K  368K       8 [SWAP]

スワップの使用量は 2.5MB(768K + 768K + 512K + 512K)です。これは Linux Mint のシステムモニターの値と一致しています。

しかし、zramctl コマンドの出力結果は難解です。

「COMPR」が圧縮後の格納データ容量らしいのですが、swapon コマンドの結果と一致していません。「TOTAL」さえも一致しておらず、はたして swapon コマンドが認識しているスワップの使用量とは何を示しているのやら、です。

またスワップアウトされた時に様子を見てみることにします。