Wydajność linkera związana z przestrzenią wymiany?
Czasami przydaje się makieta czegoś z małym programem C, który wykorzystuje dużą część pamięci statycznej. Zauważyłem, że po zmianie Na Fedora 15 Program zajął długi czas, aby / align = "left" / Mówimy o 30s vs. 0.1 S. jeszcze bardziej dziwne było to, że ld (the linker) maxing się CPU i powoli zaczął jeść wszystkie dostępne pamięć. Po kilku skrzypkach udało mi się aby znaleźć korelację między tym nowym problemem a rozmiarem mojego swapu plik. Oto przykładowy program do Dyskusja:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define M 1000000
#define GIANT_SIZE (200*M)
size_t g_arr[GIANT_SIZE];
int main( int argc, char **argv){
int i;
for(i = 0; i<10; i++){
printf("This should be zero: %d\n",g_arr[i]);
}
exit(1);
}
Ten program ma gigantyczną tablicę, która ma zadeklarowany rozmiar około 200 * 8MB = 1,6 GB pamięci statycznej. Kompilowanie tego programu zajmuje inordinate amount of time:
[me@bleh]$ time gcc HugeTest.c
real 0m12.954s
user 0m6.995s
sys 0m3.890s
[me@bleh]$
13s dla programu linii C ~13!? To nie w porządku. Numer klucza to wielkość przestrzeni pamięci statycznej. Jak tylko jest większy niż całkowita przestrzeń wymiany, zaczyna się szybko kompilować ponownie. Na przykład mieć 5.3 GB przestrzeni wymiany, więc zmiana GIANT_SIZE na (1000 * M) daje po czas:
[me@bleh]$ time gcc HugeTest.c
real 0m0.087s
user 0m0.026s
sys 0m0.027s
To mi się podoba! Do dalszego przekonania siebie (i siebie, jeśli
próbujesz tego w domu), że przestrzeń wymiany była rzeczywiście magiczna
numer, próbowałem zmienić dostępną przestrzeń wymiany na naprawdę masywną
19GB i próbuje ponownie skompilować (1000 * M) wersję:
[me@bleh]$ ls -ali /extraswap
5986 -rw-r--r-- 1 root root 14680064000 Jul 26 15:01 /extraswap
[me@bleh]$ sudo swapon /extraswap
[me@bleh]$ time gcc HugeTest.c
real 4m28.089s
user 0m0.016s
sys 0m0.010s
Nie udało się nawet po 4,5 minucie!
Najwyraźniej linker robi tu coś złego, ale nie wiem jak aby obejść to inaczej niż przepisanie programu lub bałaganu wokół z miejscem wymiany. Chciałbym wiedzieć, czy jest jakieś rozwiązanie, czy mam natknąłem się na tajemniczego robaka.
Nawiasem mówiąc, wszystkie programy kompilują się i działają poprawnie, niezależnie od wszystkich transakcji swap.
Dla odniesienia, oto kilka prawdopodobnie istotnych informacji:
[]$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 27027
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 1024
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
[]$ uname -r
2.6.40.6-0.fc15.x86_64
[]$ ld --version
GNU ld version 2.21.51.0.6-6.fc15 20110118
Copyright 2011 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.
[]$ gcc --version
gcc (GCC) 4.6.1 20110908 (Red Hat 4.6.1-9)
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
[]$ cat /proc/meminfo
MemTotal: 3478272 kB
MemFree: 1749388 kB
Buffers: 16680 kB
Cached: 212028 kB
SwapCached: 368056 kB
Active: 489688 kB
Inactive: 942820 kB
Active(anon): 401340 kB
Inactive(anon): 803436 kB
Active(file): 88348 kB
Inactive(file): 139384 kB
Unevictable: 32 kB
Mlocked: 32 kB
SwapTotal: 19906552 kB
SwapFree: 17505120 kB
Dirty: 172 kB
Writeback: 0 kB
AnonPages: 914972 kB
Mapped: 60916 kB
Shmem: 1008 kB
Slab: 55248 kB
SReclaimable: 26720 kB
SUnreclaim: 28528 kB
KernelStack: 3608 kB
PageTables: 63344 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 21645688 kB
Committed_AS: 11208980 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 139336 kB
VmallocChunk: 34359520516 kB
HardwareCorrupted: 0 kB
AnonHugePages: 151552 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 730752 kB
DirectMap2M: 2807808 kB
TL; DR: gdy (duĹźa) pamiÄ ™ Ä ‡ statyczna programu c jest nieco mniejsza niĹź dostÄ ™ pna przestrzeń wymiany, Ĺ 'Ä ... czenie programu trwa wiecznoĺ" ć. Jest jednak dość Żwawy, gdy przestrzeń statyczna jest nieco większe niż dostępna przestrzeń wymiany. O co chodzi!?
3 answers
Jestem w stanie odtworzyć to na systemie Ubuntu 10.10 (GNU ld (GNU Binutils for Ubuntu) 2.20.51-system.20100908
) i chyba mam Twoją odpowiedź. Po pierwsze, jakaś metodologia.
Po potwierdzeniu, że dzieje się to w małej maszynie wirtualnej (512MB ram, 2GB swap), stąd zdecydowałem, że najłatwiej będzie strace gcc i zobaczyć, co dokładnie się dzieje, gdy wszystko poszło do piekła:
~# strace -f gcc swap.c
Podświetlał:
vfork() = 3589
[pid 3589] execve("/usr/lib/gcc/x86_64-linux-gnu/4.4.5/collect2", ["/usr/lib/gcc/x86_64-linux-gnu/4."..., "--build-id", "--eh-frame-hdr", "-m", "elf_x86_64", "--hash-style=gnu", "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2", "-o", "swap", "-z", "relro", "/usr/lib/gcc/x86_64-linux-gnu/4."..., "/usr/lib/gcc/x86_64-linux-gnu/4."..., "/usr/lib/gcc/x86_64-linux-gnu/4."..., "-L/usr/lib/gcc/x86_64-linux-gnu/"..., ...], [/* 26 vars */]) = 0
...
[pid 3589] vfork() = 3590
...
[pid 3590] execve("/usr/bin/ld", ["/usr/bin/ld", "--build-id", "--eh-frame-hdr", "-m", "elf_x86_64", "--hash-style=gnu", "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2", "-o", "swap", "-z", "relro", "/usr/lib/gcc/x86_64-linux-gnu/4."..., "/usr/lib/gcc/x86_64-linux-gnu/4."..., "/usr/lib/gcc/x86_64-linux-gnu/4."..., "-L/usr/lib/gcc/x86_64-linux-gnu/"..., ...], [/* 27 vars */]) = 0
...
[pid 3590] lseek(13, 4096, SEEK_SET) = 4096
[pid 3590] read(13, ".\4@\0\0\0\0\0>\4@\0\0\0\0\0N\4@\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096
[pid 3590] mmap(NULL, 1600004096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1771931000
<system comes to screeching halt>
Wydaje się, że, jak mogliśmy podejrzewać, wygląda na to, że ld
jest w rzeczywistości próbuje anonimowo mmap
całą statyczną przestrzeń pamięci tej tablicy (lub ewentualnie cały program, trudno powiedzieć, ponieważ reszta programu jest tak mała, że może zmieścić się w tym dodatkowym 4096).
Więc to wszystko dobrze i dobrze, ale dlaczego to działa, gdy przekraczamy dostępne swap w systemie? Obróćmy swapoff
i pobiegnijmy strace -f
Jeszcze raz...
[pid 3618] lseek(13, 4096, SEEK_SET) = 4096
[pid 3618] read(13, ".\4@\0\0\0\0\0>\4@\0\0\0\0\0N\4@\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096
[pid 3618] mmap(NULL, 1600004096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
[pid 3618] brk(0x60638000) = 0x1046000
[pid 3618] mmap(NULL, 1600135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
[pid 3618] mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7fd011864000
...
Nic dziwnego, ld wydaje się robić to samo, co poprzednio, aby zmapować całą przestrzeń. ale system nie jest już / align = "left" / ld próbuje ponownie, i znowu się nie udaje, potem LD robi coś nieoczekiwanego... idzie dalej z mniejszą pamięcią.
Dziwne, chyba lepiej przyjrzyjmy się ld
kod . Cholera, to nie robi wyraźnego mmap
. To musi pochodzić z wnętrza zwykłego starego malloc
. Będziemy musieli zbudować ld z symbolami debugowania, żeby to namierzyć. Niestety, kiedy zbudowałem bin-utils 2.21.1 problem zniknął. Być może został naprawiony w nowszych wersjach bin-utils?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-27 03:17:27
Testowałem OpenSUSE 11.4 (za tydzień 12.1)
Mam 4GiB ram + 2gib swap i nie zauwazylem powaznego spowolnienia, system moze czasami sie rozwalac, ale i tak czas kompilacji byl krótki.
Najdłuższy był 6 sekund podczas ciężkiej wymiany.
[tester@ulises ~]$ free -m
total used free shared buffers cached
Mem: 3456 3426 30 0 4 249
-/+ buffers/cache: 3172 284
Swap: 2055 1382 672
[tester@ulises ~]$ time cc -Wall -O test2.c
test2.c: In function ‘main’:
test2.c:13:2: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘size_t’
real 0m6.501s
user 0m0.101s
sys 0m0.078s
[tester@ulises ~]$ free -m
total used free shared buffers cached
Mem: 3456 3389 67 0 5 289
-/+ buffers/cache: 3094 362
Swap: 2055 1455 599
[tester@ulises ~]$ free -m
total used free shared buffers cached
Mem: 3456 3373 82 0 4 264
-/+ buffers/cache: 3104 352
Swap: 2055 1442 612
[tester@ulises ~]$ time cc -Wall -O test2.c
test2.c: In function ‘main’:
test2.c:13:2: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘size_t’
real 0m1.122s
user 0m0.086s
sys 0m0.045s
[tester@ulises ~]$ time cc -Wall -O test2.c
test2.c: In function ‘main’:
test2.c:13:2: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘size_t’
real 0m0.095s
user 0m0.047s
sys 0m0.032s
[tester@ulises ~]$ free -m
total used free shared buffers cached
Mem: 3456 3376 79 0 4 252
-/+ buffers/cache: 3119 336
Swap: 2055 1436 618
[tester@ulises ~]$ time cc -Wall -O test2.c
test2.c: In function ‘main’:
test2.c:13:2: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘size_t’
real 0m0.641s
user 0m0.054s
sys 0m0.040s
Pomiędzy uruchomieniem załadowałem i rozładowałem Virtualbox Box VM, Eclipse, duże pliki pdf, sam mi firefox za pomocą 800 + MiB. Nie przekroczyłem limitu, w przeciwnym razie wiele aplikacji zostałoby zabitych przez system operacyjny. Informatyka preferuje zabijanie Firefoksa.. :-)
Przeszedłem też do skrajnego zdefiniowania:
#define M 1048576
#define GIANT_SIZE (20000*M)
I nawet wtedy nic się nie zmienia.
[tester@ulises ~]$ time cc -Wall -O test2.c
test2.c:7:14: warning: integer overflow in expression
test2.c:7:8: error: size of array ‘g_arr’ is negative
test2.c:7:1: warning: variably modified ‘g_arr’ at file scope
test2.c: In function ‘main’:
test2.c:13:2: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘size_t’
real 0m0.661s
user 0m0.043s
sys 0m0.031s
Edytuj: Ponownie przetestowałem Fedora16 na maszynie wirtualnej z 512MIB RAM i wymianą 1.5 GiB i rzeczy były podobne, z wyjątkiem Komunikatu o błędzie w mojej" wersji maksymalnego stresu", gdzie 20000 megabajtów zostało przypisanych do tablicy. Błąd mówi, że rozmiar tablicy był ujemny.
[ricardo@localhost ~]$ time gcc -Wall test2.c
test2.c:7:14: warning: integer overflow in expression [-Woverflow]
test2.c:7:8: error: size of array ‘g_arr’ is negative
test2.c:7:1: warning: variably modified ‘g_arr’ at file scope [enabled by default]
test2.c: In function ‘main’:
test2.c:13:2: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘size_t’ [-Wformat]
real 0m1.053s
user 0m0.050s
sys 0m0.137s
Ta sama odpowiedź ma miejsce w OpenSUSE 12.1 VM. Fedora 16 install seamed verry slow and memory hungry (podczas instalacji musiałem użyć 800MiB versus OpenSuse 512 MiB), nie mogłem użyć swapoff na Fedorze, ponieważ używało dużo miejsca wymiany. Nie miałem problemów z pamięcią na OpenSuse 12.1 i . Oba mają zasadniczo te same wersje jądra, gcc itp. Zarówno przy użyciu stockowych instalacji z KDE jako środowiskiem graficznym
Nie mogłem odtworzyć problemów, może jest to problem związany z gcc. Spróbuj pobrać starszą wersję jak 4.5 i zobacz co się dzieje
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-18 22:32:26
Nie obserwuję tego zachowania (z Debian / Sid / AMD64 na pulpicie 8Gb, gcc 4.6.2, binutils gold LD (GNU Binutils for Debian 2.22) 1.11). Oto zmieniony program (wyświetlający jego mapę pamięci za pomocą pmap
).
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define M 1000000
#define GIANT_SIZE (2000*M)
size_t g_arr[GIANT_SIZE];
int main( int argc, char **argv){
int i;
char cmd[80];
for(i = 0; i<10; i++){
printf("This should be zero: %d\n",g_arr[i*1000]);
}
sprintf (cmd, "pmap %d", (int)getpid());
system(cmd);
exit(0);
}
Oto jego kompilacja:
% time gcc -v -O big.c -o big
Using built-in specs.
COLLECT_GCC=/usr/bin/gcc-4.6.real
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.6/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.6.2-4' --with-bugurl=file:///usr/share/doc/gcc-4.6/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++,go --prefix=/usr --program-suffix=-4.6 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.6 --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-plugin --enable-objc-gc --with-arch-32=i586 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.6.2 (Debian 4.6.2-4)
COLLECT_GCC_OPTIONS='-v' '-O' '-o' 'big' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-linux-gnu/4.6/cc1 -quiet -v -imultilib . -imultiarch x86_64-linux-gnu big.c -quiet -dumpbase big.c -mtune=generic -march=x86-64 -auxbase big -O -version -o /tmp/ccWThBP5.s
GNU C (Debian 4.6.2-4) version 4.6.2 (x86_64-linux-gnu)
compiled by GNU C version 4.6.2, GMP version 5.0.2, MPFR version 3.1.0, MPC version 0.9
warning: MPFR header version 3.1.0 differs from library version 3.1.0-p3.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/4.6/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/4.6/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
GNU C (Debian 4.6.2-4) version 4.6.2 (x86_64-linux-gnu)
compiled by GNU C version 4.6.2, GMP version 5.0.2, MPFR version 3.1.0, MPC version 0.9
warning: MPFR header version 3.1.0 differs from library version 3.1.0-p3.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 4b128876859f8f310615c7040fa3cb67
COLLECT_GCC_OPTIONS='-v' '-O' '-o' 'big' '-mtune=generic' '-march=x86-64'
as --64 -o /tmp/ccm7905b.o /tmp/ccWThBP5.s
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.6/:/usr/lib/gcc/x86_64-linux-gnu/4.6/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.6/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.6/:/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-O' '-o' 'big' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-linux-gnu/4.6/collect2 --build-id --no-add-needed --eh-frame-hdr -m elf_x86_64 --hash-style=both -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o big /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.6/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.6 -L/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/4.6/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.6/../../.. /tmp/ccm7905b.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/4.6/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crtn.o
gcc -v -O big.c -o big 0.07s user 0.01s system 90% cpu 0.089 total
I jego wykonanie:
% time ./big
This should be zero: 0
This should be zero: 0
This should be zero: 0
This should be zero: 0
This should be zero: 0
This should be zero: 0
This should be zero: 0
This should be zero: 0
This should be zero: 0
This should be zero: 0
8835: ./big
0000000000400000 4K r-x-- /home/basile/tmp/big
0000000000401000 4K rw--- /home/basile/tmp/big
0000000000402000 15625000K rw--- [ anon ]
00007f2d15a44000 1512K r-x-- /lib/x86_64-linux-gnu/libc-2.13.so
00007f2d15bbe000 2048K ----- /lib/x86_64-linux-gnu/libc-2.13.so
00007f2d15dbe000 16K r---- /lib/x86_64-linux-gnu/libc-2.13.so
00007f2d15dc2000 4K rw--- /lib/x86_64-linux-gnu/libc-2.13.so
00007f2d15dc3000 20K rw--- [ anon ]
00007f2d15dc8000 124K r-x-- /lib/x86_64-linux-gnu/ld-2.13.so
00007f2d15fb4000 12K rw--- [ anon ]
00007f2d15fe4000 12K rw--- [ anon ]
00007f2d15fe7000 4K r---- /lib/x86_64-linux-gnu/ld-2.13.so
00007f2d15fe8000 4K rw--- /lib/x86_64-linux-gnu/ld-2.13.so
00007f2d15fe9000 4K rw--- [ anon ]
00007ffff5b5b000 132K rw--- [ stack ]
00007ffff5bff000 4K r-x-- [ anon ]
ffffffffff600000 4K r-x-- [ anon ]
total 15628908K
./big 0.00s user 0.00s system 0% cpu 0.004 total
Uważam, że instalacja najnowszego GCC (np. GCC 4.6) z Binutils Gold linker jest istotna dla takich programów.
Nie słyszę żadnej zamiany zaangażowany.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-22 20:52:16