Optymalizacja użycia pamięci w Linuksie

W tym miejscu zapraszamy Was do współpracy. Czekamy na propozycje, sugestie i rady.
Moderatorzy zatroszczą się o to, by najlepsze teksty trafiły do FAQ.

Moderatorzy: Moderatorzy, Administratorzy

Awatar użytkownika
argon
Użytkownik
Posty: 240
Rejestracja: 2004-09-29, 00:49
Lokalizacja: Węgorzewo
Kontakt:

Optymalizacja użycia pamięci w Linuksie

Post autor: argon »

Optymalizacja użycia pamięci w Linuksie

0. Cel
Komfortowe używanie systemu Linux na maszynach ograniczonych w pamięć oraz/lub bez swap-a, także na zwykłych desktopach.

1. Sprzęt testowy:
CPU: AMD K6-II 500MHz
RAM: 192 MiB
swap: 487.6 MiB
kernel: Linux Flame 2.6.18.8-flame #2 PREEMPT Sat Nov 24 23:56:42 CET 2007 i586 k6-2 i386 GNU/Linux
OS: Slackware Linux 11.0

2. Testy
Skorzystałem z instrukcji na stronie: http://www.win.tue.nl/~aeb/linux/lk/lk-9.html
Znajdują się tam trzy programy testowe:
Program 1, alokujący pamięć, lecz jej nie używający:

Kod: Zaznacz cały

#include <stdio.h>
#include <stdlib.h>

int main (void) {
        int n = 0;

        while (1) {
                if (malloc(1<<20) == NULL) {
                        printf("malloc failure after %d MiB\n", n);
                        return 0;
                }
                printf ("got %d MiB\n", ++n);
        }
}
Program 2, alokujący pamięć i "dotykający" jej:

Kod: Zaznacz cały

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main (void) {
        int n = 0;
        char *p;

        while (1) {
                if (malloc(1<<20) == NULL) {
                        printf("malloc failure after %d MiB\n", n);
                        return 0;
                }
                memset (p, 0, (1<<20));
                printf ("got %d MiB\n", ++n);
        }
}
Program 3, najpierw alokujący, później używający pamięci:

Kod: Zaznacz cały

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define N       10000

int main (void) {
        int i, n = 0;
        char *pp[N];

        for (n = 0; n < N; n++) {
                pp[n] = malloc(1<<20);
                if (pp[n] == NULL)
                        break;
        }
        printf("malloc failure after %d MiB\n", n);

        for (i = 0; i < n; i++) {
                memset (pp[i], 0, (1<<20));
                printf("%d\n", i+1);
        }

        return 0;
}
Na początku program1 dostaje bardzo dużo RAMu:

Kod: Zaznacz cały

got 3056 MiB
malloc failure after 3056 MiB
To jest optymistyczne zachowanie Linuksa, uważającego, że programy alokują więcej pamięci niż im naprawdę potrzeba. Nazywa się to heurystyczny overcommit. Jak zobaczymy dalej, nie zawsze to jest dobre...

program2 kończy tak:

Kod: Zaznacz cały

got 166 MiB
Unicestwiony
syslog:[code]Flame kernel: Out of Memory: Kill process 2526 (program2) score 43273 and children.
Flame kernel: Out of memory: Killed process 2526 (program2).[/code]Out-Of-Memory-killer "zajął się" aplikacją, która dostała za dużo pamięci...

program3:

Kod: Zaznacz cały

malloc failure after 3056 MiB
1
2
[...]
153
Unicestwiony
...też ubity przez OOM killera. Autor strony, z której korzystam (nb. nieco już wiekowej) uważa istnienie OOM killera za błąd (bug)! Fakt, może się zdarzyć, że w przypadku braku pamięci system ubije coś innego niż źle zachowującą się aplikację...

Mnie osobiście kojarzy się to ze sprzedarzą gruntu: ludzie kupują działki, których nie ma. Wszystko jest OK do czasu, gdy wszyscy tam pojadą i zacznie brakować ziemi. Wtedy do akcji wkracza były właściciel ze strzelbą i odstrzeliwuje tych, którzy mu się nie podobają, żeby inni mieli tyle, ile kupili :>

3. Humanizacja zachowania
Zawartość pliku:

Kod: Zaznacz cały

/proc/sys/vm/overcommit_memory
reguluje kiedy kernel ma kłamać odnośnie dostępnej pamięci:

Kod: Zaznacz cały

0: heurystyczny overcommit (domyślnie)
1: zawsze robi overcommit, nigdy nie sprawdza (jeszcze gorzej niż 0)
2: zawsze sprawdza, nigdy nie robi overcommitu
Systemy takie jak Solaris domyślnie zachowują się według schematu 2. Zobaczmy co się stanie:
program1:[code][...]
got 540 MiB
malloc failure after 540 MiB[/code]
program2:[code][...]
got 540 MiB
malloc failure after 540 MiB[/code]
program3:[code]malloc failure after 540 MiB
1
2
[...]
540[/code]
Lepiej. Jednak po wyłączeniu swap-a każdy program dostaje 69 MiB RAMu, mimo że free pokazuje 157 MiB. Wzór na dostępną pamięć wirtualną to:

Kod: Zaznacz cały

pamięć = (SS + RAM*(r/100))
gdzie:

Kod: Zaznacz cały

SS:  rozmiar swap-u
RAM: rozmiar RAMu
r:   zawartość pliku /proc/sys/vm/overcommit_ratio
Po:

Kod: Zaznacz cały

echo 100 > /proc/sys/vm/overcommit_ratio
wszystkie programy dostają 157 MiB RAMu, zaś po włączeniu swap-a: 628 MiB

4. Konkluzja
Nie jestem pewien czy wpisanie wartości 100 to dobry pomysł, może lepiej zostawić parę MiB kernelowi. Teraz wpisałem 90 na swoim desktopie (RAM: 768 MiB, swap: 972 MiB).

Tekst był pisany na gorąco, w miarę testów i uwag postaram się go uzupełnić i poprawić błędy.
Mam nadzieję, że będzie to pomocne, zapraszam do testów :)
--
Zobacz też:
http://rudd-o.com/archives/2007/10/02/t ... -fix-that/

Źródła:
http://www.win.tue.nl/~aeb/linux/lk/lk-9.html
http://man.cx/proc(5)/pl
Opowiadanie: Kirył Bułyczow - Są wolne miejsca
Ostatnio zmieniony 2007-12-01, 20:29 przez argon, łącznie zmieniany 3 razy.
No RISC, no fun!
Awatar użytkownika
xil
Moderator
Posty: 862
Rejestracja: 2004-06-20, 22:20
Lokalizacja: Białystok
Kontakt:

Re: Optymalizacja użycia pamięci w Linuksie

Post autor: xil »

Fakt, może się zdarzyć, że w przypadku braku pamięci system ubije coś innego niż źle zachowującą się aplikację...
moja magisterka w przyplywie emocji chciala zjesc caly ram razem z laptopem... i tak wlasnie bylo - kilowane byly rozne programy.. dopiero po jakims czasie upolowano wlasciwy proces...
Awatar użytkownika
Lizard
Moderator
Posty: 2629
Rejestracja: 2005-05-21, 15:48
Lokalizacja: miasto w mieście

Re: Optymalizacja użycia pamięci w Linuksie

Post autor: Lizard »

W programach 1. i 2. jest ten sam błąd: alokacja pamięci bez przypisania adresu tejże do wskaźnika. O ile w programie 1. ten manewr przejdzie (chociaż za taka praktykę należałoby rozstrzelać), tak 2. wyłoży się przy funkcji memset() (kompilator wcześniej zwróci ostrzeżenie o wykorzystaniu zmiennej przed inicjacją).
Error 404 - footer not found
Awatar użytkownika
argon
Użytkownik
Posty: 240
Rejestracja: 2004-09-29, 00:49
Lokalizacja: Węgorzewo
Kontakt:

Re: Optymalizacja użycia pamięci w Linuksie

Post autor: argon »

To są przykładowe programy (chodzi o ideę), prawda, pamięć zbierają "do worka". Co innego gdyby tak ktoś postąpił w prawdziwej aplikacji :>
No RISC, no fun!
ODPOWIEDZ