Oleh karena itu, hari ini kita akan melakukannya .. Sebuah pekerjaan laboratorium kecil. Dalam bentuk program C kecil yang kami tulis, kompilasi, dan uji secara langsung - dengan dan tanpa swap.
Program ini melakukan hal yang sangat sederhana - ia meminta sebagian besar memori, mengaksesnya dan secara aktif bekerja dengannya. Agar tidak menderita saat memuat pustaka apa pun, kami hanya akan membuat file besar yang akan dipetakan ke dalam memori seperti yang dilakukan sistem saat memuat pustaka bersama.
Dan kami hanya meniru panggilan kode dari "perpustakaan" ini dengan membaca dari file mmap tersebut.
Program akan membuat beberapa iterasi, pada setiap iterasi secara bersamaan akan mengakses "kode" dan salah satu bagian dari segmen data yang besar.
Dan, agar tidak menulis kode yang tidak perlu, kami akan menentukan dua konstanta yang akan menentukan ukuran "segmen kode" dan ukuran total RAM:
- MEM_GBYTES - ukuran RAM untuk pengujian
- LIB_GBYTES - ukuran "kode"
Jumlah "data" yang kami miliki kurang dari jumlah memori fisik:
- DATA_GBYTES = MEM_GBYTES - 2
Jumlah total "kode" dan "data" sedikit lebih besar daripada jumlah memori fisik:
- DATA_GBYTES + LIB_GBYTES = MEM_GBYTES + 1
Untuk tes di laptop, saya mengambil MEM_GBYTES = 16, dan mendapatkan karakteristik berikut:
- MEM_GBYTES = 16
- DATA_GBYTES = 14 - berarti "data" akan menjadi 14GB, itu adalah "memori yang cukup"
- Ukuran swap = 16GB
Teks program
#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;
}
Uji tanpa menggunakan swap
Nonaktifkan swap dengan menentukan vm.swappines = 0 dan jalankan pengujian
$ time ./swapdemo Preparing test ... Killed real 0m6,279s user 0m0,459s sys 0m5,791s
Apa yang terjadi? Nilai swappiness = 0 menonaktifkan swap - halaman anonim tidak lagi didorong ke dalamnya, yaitu, data selalu ada di memori. Masalahnya adalah bahwa sisa 2GB tidak cukup untuk Chrome dan VSCode yang berjalan di latar belakang, dan OOM-killer mematikan program pengujian. Dan pada saat yang sama, kurangnya memori membenamkan tab Chrome tempat saya menulis artikel ini. Dan saya tidak menyukainya - meskipun penyimpanan otomatis berfungsi. Saya tidak suka jika data saya terkubur.
Swap disertakan
Setel vm_swappines = 60 (default)
Jalankan pengujian:
$ 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
Fragmen atas:
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 buruk, buruk !!! Ini menggunakan hampir 4 gigabyte swap meskipun memiliki 14 gigabyte cache dan 3 gigabyte tersedia! Linux memiliki pengaturan yang salah! Outlingo buruk, admin lama yang buruk, mereka tidak mengerti apa-apa, mereka mengatakan untuk mengaktifkan swap dan sekarang mereka membuat sistem bertukar dan bekerja dengan buruk untuk saya. Anda perlu menonaktifkan swap seperti yang disarankan oleh pakar Internet yang jauh lebih muda dan menjanjikan, karena mereka tahu persis apa yang harus dilakukan!
Baiklah ... biarlah. Mari kita matikan swap sebanyak mungkin atas saran para ahli?
Uji hampir tanpa swap
Kami menetapkan vm_swappines = 1
Nilai ini akan mengarah pada fakta bahwa pertukaran halaman anonim hanya akan dilakukan jika tidak ada jalan keluar lain.
Saya mempercayai Chris Down karena menurut saya dia adalah insinyur yang hebat dan tahu apa yang dia katakan saat dia menjelaskan bahwa file swap membuat sistem bekerja lebih baik. Oleh karena itu, mengharapkan bahwa "sesuatu" akan "salah" dan sistem mungkin bekerja sangat tidak efisien, saya memastikan sebelumnya dan menjalankan program pengujian, membatasinya dengan pengatur waktu untuk melihat setidaknya penghentian abnormal.
Mari kita lihat hasil teratas terlebih dahulu:
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
Hore ?! Swap hanya digunakan untuk 2,5 gigabyte, yang hampir 2 kali lebih kecil daripada saat pengujian dengan swap diaktifkan (dan swappiness = 60). Swap digunakan lebih sedikit. Memori bebas juga lebih sedikit. Dan kami mungkin bisa dengan aman memberikan kemenangan kepada para ahli muda. Tetapi inilah yang aneh - program kami tidak pernah dapat menyelesaikan bahkan 1 (SATU!) Iterasi dalam 2 (DUA!) Menit:
$ { 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
Kami ulangi - program tidak dapat menyelesaikan 1 iterasi dalam 2 menit, meskipun pada pengujian sebelumnya ia melakukan 11 iterasi dalam 2 menit - yaitu, dengan swap hampir dinonaktifkan, program berjalan lebih dari 10 (!) Kali lebih lambat.
Tetapi ada satu nilai tambah - tidak ada satu pun tab Chrome yang dirugikan. Dan ini bagus.
Uji dengan menonaktifkan swap sepenuhnya
Tapi mungkin hanya "menghancurkan" swap melalui swappiness saja tidak cukup, dan itu harus benar-benar dinonaktifkan? Tentu, teori ini juga harus diuji. Kami datang ke sini untuk melakukan tes, atau apa?
Ini adalah kasus yang ideal:
- kami tidak memiliki swap dan semua data kami akan dijamin di memori
- swap tidak akan digunakan bahkan secara tidak sengaja, karena tidak ada
Dan sekarang pengujian kami akan berakhir dengan kecepatan kilat, orang tua akan pergi ke tempat yang layak mereka terima dan akan mengganti selongsong peluru - cara untuk kaum muda.
Sayangnya, hasil dari menjalankan program pengujian serupa - bahkan tidak ada satu iterasi yang diselesaikan.
Hasil teratas:
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
Mengapa ini terjadi
Penjelasannya sangat sederhana - "segmen kode" yang kita hubungkan melalui mmap (libptr) ada di cache. Oleh karena itu, ketika kami melarang (atau hampir melarang) swap dengan satu atau lain cara, tidak peduli bagaimana caranya - dengan menonaktifkan swap secara fisik, atau melalui vm.swappines = 0 | 1 - itu selalu berakhir dengan skenario yang sama - membersihkan file mmap dari cache dan kemudian memuatnya dari disk. Dan pustaka dimuat persis melalui mmap, dan untuk memverifikasi ini, Anda hanya perlu melakukan 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
Dan, seperti yang kita bahas di bagian pertama artikel ini, sistem, dalam kondisi kekurangan memori yang sebenarnya, ketika penukaran halaman anonim dinonaktifkan, akan memilih satu-satunya opsi yang ditinggalkan oleh pemilik yang menonaktifkan penukaran. Dan opsi ini mengklaim kembali (membebaskan) halaman kosong yang ditempati oleh data pustaka yang dimuat mmap.
Kesimpulan
Penggunaan aktif metode distribusi perangkat lunak βI take everything with meβ (flatpak, snap, docker image) mengarah pada fakta bahwa jumlah kode yang terhubung melalui mmap meningkat secara signifikan.
Hal ini dapat mengarah pada fakta bahwa penggunaan "pengoptimalan ekstrem" yang terkait dengan pengaturan / penonaktifan swap dapat menyebabkan efek yang sama sekali tidak terduga, karena file swap adalah mekanisme untuk mengoptimalkan subsistem memori virtual dalam kondisi tekanan memori, dan memori yang tersedia sepenuhnya bukan "memori yang tidak terpakai" tetapi jumlah cache dan memori bebas.
Dengan menonaktifkan file swap, Anda tidak "menghapus opsi yang salah", tetapi "tidak meninggalkan opsi"
Anda harus sangat berhati-hati saat menafsirkan data konsumsi memori proses - VSS dan RSS. Mereka mewakili "keadaan saat ini" dan bukan "keadaan optimal".
Jika Anda tidak ingin sistem menggunakan swap, tambahkan memori ke dalamnya, tetapi jangan nonaktifkan swap . Menonaktifkan swap pada level ambang akan membuat situasinya jauh lebih buruk daripada jika sistem telah ditukar sedikit.
PS: Dalam diskusi, pertanyaan sering diajukan "tetapi jika Anda mengaktifkan kompresi memori melalui zram ...". Saya penasaran, dan saya menjalankan tes yang sesuai: jika Anda mengaktifkan zram dan swap, seperti yang dilakukan secara default di Fedora, maka runtime akan dipercepat menjadi sekitar 1 menit.
Tetapi alasan untuk ini adalah bahwa halaman dengan nol dikompresi dengan sangat baik, jadi sebenarnya data tidak masuk untuk ditukar, tetapi disimpan dalam bentuk terkompresi di RAM. Jika Anda mengisi segmen data dengan data acak yang tidak dapat dikompres dengan baik, gambar akan menjadi kurang spektakuler dan waktu pengujian akan meningkat lagi menjadi 2 menit, yang sebanding (dan bahkan sedikit lebih buruk) dibandingkan dengan file swap yang "jujur".