したがって、今日は.. 。小さな実験室の仕事。スワップの有無にかかわらず、実際に作成、コンパイル、テストする小さなCプログラムの形式で。
プログラムは非常に単純なことを行います-それはメモリの大きなチャンクを要求し、それにアクセスし、そしてそれを積極的に操作します。ライブラリのロードに悩まされないようにするために、共有ライブラリをロードするときのシステムと同じように、メモリにマップされる大きなファイルを作成するだけです。
そして、このようなmmapファイルから読み取ることにより、この「ライブラリ」からのコードの呼び出しをエミュレートするだけです。
プログラムは複数の反復を行い、各反復で「コード」と大きなデータセグメントのセクションの1つに同時にアクセスします。
また、不要なコードを記述しないために、「コードセグメント」のサイズとRAMの合計サイズを決定する2つの定数を定義します。
- MEM_GBYTES-テスト用のRAMのサイズ
- LIB_GBYTES-「コード」サイズ
私たちが持っている「データ」の量は、物理メモリの量よりも少ないです。
- DATA_GBYTES = MEM_GBYTES-2
「コード」と「データ」の合計量は、物理メモリの量よりもわずかに多くなります。
- DATA_GBYTES + LIB_GBYTES = MEM_GBYTES + 1
ラップトップでのテストでは、MEM_GBYTES = 16を使用し、次の特性を取得しました。
- MEM_GBYTES = 16
- DATA_GBYTES = 14-「データ」が14GB、つまり「十分なメモリ」になることを意味します
- スワップサイズ= 16GB
プログラムテキスト
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#define GB 1073741824l
#define MEM_SIZE 16
#define LIB_GBYTES 3
#define DATA_GBYTES (MEM_SIZE - 2)
long random_read(char * code_ptr, char * data_ptr, size_t size) {
long rbt = 0;
for (unsigned long i=0 ; i<size ; i+=4096) {
rbt += code_ptr[(8l * random() % size)] + data_ptr[i];
}
return rbt;
}
int main() {
size_t libsize = LIB_GBYTES * GB;
size_t datasize = DATA_GBYTES * GB;
int fd;
char * dataptr;
char * libptr;
srandom(256);
if ((fd = open("library.bin", O_RDONLY)) < 0) {
printf("Required library.bin of size %ld\n", libsize);
return 1;
}
if ((libptr = mmap(NULL, libsize,
PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
printf("Failed build libptr due %d\n", errno);
return 1;
}
if ((dataptr = mmap(NULL, datasize,
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
-1, 0)) == MAP_FAILED) {
printf("Failed build dataptr due %d\n", errno);
return 1;
}
printf("Preparing test ...\n");
memset(dataptr, 0, datasize);
printf("Doing test ...\n");
unsigned long chunk_size = GB;
unsigned long chunk_count = (DATA_GBYTES - 3) * GB / chunk_size;
for (unsigned long chunk=0 ; chunk < chunk_count; chunk++) {
printf("Iteration %d of %d\n", 1 + chunk, chunk_count);
random_read(libptr, dataptr + (chunk * chunk_size), libsize);
}
return 0;
}
スワップを使用せずにテストする
vm.swappines = 0を指定してスワップを無効にし、テストを実行します
$ time ./swapdemo Preparing test ... Killed real 0m6,279s user 0m0,459s sys 0m5,791s
何が起こった?swappiness value = 0は、スワップを無効にしました。匿名ページはスワップにプッシュされなくなりました。つまり、データは常にメモリにあります。問題は、残りの2GBがChromeとVSCodeをバックグラウンドで実行するには不十分であり、OOM-killerがテストプログラムを強制終了したことです。同時に、メモリの不足により、この記事を書いたChromeタブが埋もれてしまいました。そして、自動保存が機能したとしても、私はそれが気に入らなかった。私のデータが埋もれているとき、私はそれが好きではありません。
スワップが含まれています
Set vm_swappines = 60(デフォルト)
テストを実行します。
$ time ./swapdemo Preparing test ... Doing test ... Iteration 1 of 11 Iteration 2 of 11 Iteration 3 of 11 Iteration 4 of 11 Iteration 5 of 11 Iteration 6 of 11 Iteration 7 of 11 Iteration 8 of 11 Iteration 9 of 11 Iteration 10 of 11 Iteration 11 of 11 real 1m55,291s user 0m2,692s sys 0m20,626s
フラグメントトップ:
Tasks: 298 total, 2 running, 296 sleeping, 0 stopped, 0 zombie %Cpu(s): 0,6 us, 3,1 sy, 0,0 ni, 85,7 id, 10,1 wa, 0,5 hi, 0,0 si, 0,0 st MiB Mem : 15670,0 total, 156,0 free, 577,5 used, 14936,5 buff/cache MiB Swap: 16384,0 total, 12292,5 free, 4091,5 used. 3079,1 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 10393 viking 20 0 17,0g 14,2g 14,2g D 17,3 93,0 0:18.78 swapdemo 136 root 20 0 0 0 0 S 9,6 0,0 4:35.68 kswapd0
悪い、悪いLinux !!! 14ギガバイトのキャッシュと3ギガバイトが利用可能ですが、ほぼ4ギガバイトのスワップを使用します。Linuxの設定が間違っています!悪いoutlingo、悪い古い管理者、彼らは何も理解していません、彼らはスワップを有効にするように言いました、そして今彼らはシステムをスワップさせて私のためにひどく働きます。彼らは何をすべきかを正確に知っているので、はるかに若くて有望なインターネットの専門家のアドバイスに従ってスワップを無効にする必要があります!
ええと...そうです。専門家のアドバイスで可能な限りスワップをオフにしましょう。
ほとんどスワップなしでテスト
vm_swappines = 1に設定し
ます。この値は、匿名ページのスワップが他に方法がない場合にのみ実行されるという事実につながります。
クリス・ダウンは素晴らしいエンジニアだと思います。スワップファイルによってシステムのパフォーマンスが向上すると説明したとき、彼の言うことを知っているからです。したがって、「何か」が「うまくいかず」、システムがひどく非効率的に動作する可能性があることを期待して、事前に確認してテストプログラムを実行し、タイマーで制限して、少なくとも異常終了を確認しました。
最初に一番上の出力を見てみましょう:
Tasks: 302 total, 1 running, 301 sleeping, 0 stopped, 0 zombie %Cpu(s): 0,2 us, 4,7 sy, 0,0 ni, 84,6 id, 10,0 wa, 0,4 hi, 0,0 si, 0,0 st MiB Mem : 15670,0 total, 162,8 free, 1077,0 used, 14430,2 buff/cache MiB Swap: 20480,0 total, 18164,6 free, 2315,4 used. 690,5 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 6127 viking 20 0 17,0g 13,5g 13,5g D 20,2 87,9 0:10.24 swapdemo 136 root 20 0 0 0 0 S 17,2 0,0 2:15.50 kswapd0
やったー?スワップは2.5ギガバイトでのみ使用されます。これは、スワップを有効にしたテスト(およびswappiness = 60)のほぼ2分の1です。スワップの使用量は少なくなります。空きメモリも少なくなります。そして、私たちはおそらく若い専門家に安全に勝利を与えることができます。しかし、ここに奇妙なことがあります-私たちのプログラムは、2(TWO!)分で1(ONE!)の反復を完了することさえできませんでした:
$ { sleep 120 ; killall swapdemo ; } & [1] 6121 $ time ./swapdemo Preparing test … Doing test … Iteration 1 of 11 [1]+ Done { sleep 120; killall swapdemo; } Terminated real 1m58,791s user 0m0,871s sys 0m23,998s
繰り返します-プログラムは2分で1回の反復を完了できませんでしたが、前のテストでは2分で11回の反復を実行しました-つまり、スワップがほとんど無効になっているため、プログラムの実行速度は10(!)倍以上遅くなります。
しかし、1つのプラスがあります-単一のChromeタブが害を受けたわけではありません。そして、これは良いことです。
スワップを完全に無効にしてテストする
しかし、swappinessを介してスワップを「クラッシュ」させるだけでは不十分であり、完全に無効にする必要がありますか?当然、この理論もテストする必要があります。私たちはテストを行うためにここに来ました、または何ですか?
これは理想的なケースです。
- スワップはなく、すべてのデータはメモリ内で保証されます
- スワップは存在しないため、誤って使用されることはありません。
そして今、私たちのテストは電光石火の速さで終わります、老人は彼らが値する場所に行き、カートリッジを交換します-若者のための方法。
残念ながら、テストプログラムを実行した結果は似ており、1回の反復も完了していません。
トップ出力:
Tasks: 217 total, 1 running, 216 sleeping, 0 stopped, 0 zombie %Cpu(s): 0,0 us, 2,2 sy, 0,0 ni, 85,2 id, 12,6 wa, 0,0 hi, 0,0 si, 0,0 st MiB Mem : 15670,0 total, 175,2 free, 331,6 used, 15163,2 buff/cache MiB Swap: 0,0 total, 0,0 free, 0,0 used. 711,2 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 136 root 20 0 0 0 0 S 12,5 0,0 3:22.56 kswapd0 7430 viking 20 0 17,0g 14,5g 14,5g D 6,2 94,8 0:14.94 swapdemo
なぜこうなった
説明は非常に簡単です。mmap(libptr)を介して接続する「コードセグメント」はキャッシュにあります。したがって、何らかの方法でスワップを禁止(またはほぼ禁止)する場合、スワップを物理的に無効にするか、vm.swappines = 0 | 1を使用して、常に同じシナリオで終了します-mmapファイルをフラッシュしますキャッシュから、次にディスクからロードします。また、ライブラリはmmapを介して正確にロードされます。これを確認するには、ls -l / proc // map_filesを実行するだけです。
$ ls -l /proc/8253/map_files/ | head -n 10 total 0 lr-------- 1 viking viking 64 7 12:58 556799983000-55679998e000 -> /usr/libexec/gnome-session-binary lr-------- 1 viking viking 64 7 12:58 55679998e000-5567999af000 -> /usr/libexec/gnome-session-binary lr-------- 1 viking viking 64 7 12:58 5567999af000-5567999bf000 -> /usr/libexec/gnome-session-binary lr-------- 1 viking viking 64 7 12:58 5567999c0000-5567999c4000 -> /usr/libexec/gnome-session-binary lr-------- 1 viking viking 64 7 12:58 5567999c4000-5567999c5000 -> /usr/libexec/gnome-session-binary lr-------- 1 viking viking 64 7 12:58 7fb22a033000-7fb22a062000 -> /usr/share/glib-2.0/schemas/gschemas.compiled lr-------- 1 viking viking 64 7 12:58 7fb22b064000-7fb238594000 -> /usr/lib/locale/locale-archive lr-------- 1 viking viking 64 7 12:58 7fb238594000-7fb2385a7000 -> /usr/lib64/gvfs/libgvfscommon.so lr-------- 1 viking viking 64 7 12:58 7fb2385a7000-7fb2385c3000 -> /usr/lib64/gvfs/libgvfscommon.so
そして、記事の最初の部分で検討したように、システムは、実際にメモリが不足している状態で、匿名ページのスワップが無効になっている場合、スワップを無効にした所有者が残した唯一のオプションを選択します。そして、このオプションは、mmapがロードされたライブラリのデータによって占められていた空白ページを再利用(解放)します。
結論
「Itakeallthing with me」ソフトウェア配布方法(flatpak、snap、docker image)を積極的に使用すると、mmapを介して接続されるコードの量が大幅に増加します。
これは、スワップの設定/無効化に関連する「極端な最適化」の使用が完全に予期しない影響をもたらす可能性があるという事実につながる可能性があります。スワップファイルはメモリ不足の条件下で仮想メモリサブシステムを最適化するメカニズムであり、使用可能なメモリは完全に「未使用メモリ」ではなく、キャッシュと空きメモリの合計です。
スワップファイルを無効にすることで、「間違ったオプションを削除」するのではなく、「オプションを残さない」ことになります。
プロセスのメモリ消費データ(VSSとRSS)を解釈するときは、十分に注意する必要があります。それらは「現在の状態」を表し、「最適な状態」ではありません。
システムにスワップを使用させたくない場合は、システムに メモリを追加しますが、スワップを無効にしないでください。しきい値レベルでスワップを無効にすると、システムが少しスワップされた場合よりも状況が大幅に悪化します。
PS:ディスカッションでは、「ただし、zramを介してメモリ圧縮を有効にした場合は...」という質問が定期的に行われます。私は興味を持ち、適切なテストを実行しました。Fedoraでデフォルトで行われているように、zramとswapを有効にすると、ランタイムは約1分に加速します。
ただし、この理由は、ゼロのあるページは非常によく圧縮されているため、実際にはデータはスワップされませんが、圧縮された形式でRAMに保存されます。データセグメントをランダムで圧縮率の低いデータで埋めると、画像の見栄えが悪くなり、テストの実行時間は再び2分に増加します。これは、「正直な」スワップファイルの場合と同等(さらにはわずかに悪い)です。