Mengapa Linux menggunakan file swap, bagian 2

Bagian pertama dari sedikit "ripping off" tentang subsistem memori virtual, mekanisme koneksi mmap, perpustakaan bersama, dan cache menyebabkan diskusi yang memanas sehingga saya tidak dapat menahan diri untuk terus meneliti dalam praktik.



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".



All Articles