it-swarm.com.de

Linkerleistung im Zusammenhang mit Swap Space?

Manchmal ist es praktisch, etwas mit einem kleinen C-Programm zu verspotten, das einen großen Teil des statischen Speichers verwendet. Nachdem ich auf Fedora 15 gewechselt war, dauerte das Kompilieren des Programms eine lange Zeit. Wir sprechen von 30s vs. 0.1s. Noch seltsamer war, dass ld (der Linker) die CPU auslastete und langsam anfing, den gesamten verfügbaren Speicher zu verbrauchen. Nach einigem Hin und Her konnte ich eine Korrelation zwischen diesem neuen Problem und der Größe meiner Auslagerungsdatei finden. Hier ist ein Beispielprogramm für die Zwecke dieser Diskussion:

#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);
}

Dieses Programm verfügt über ein riesiges Array mit einer deklarierten Größe von ca. 200 * 8 MB = 1,6 GB statischem Speicher. Das Kompilieren dieses Programms nimmt übermäßig viel Zeit in Anspruch:

[[email protected]]$ time gcc HugeTest.c 

real    0m12.954s
user    0m6.995s
sys 0m3.890s

[[email protected]]$

13s Für ein C-Programm mit ~ 13 Zeilen !? Das ist nicht richtig. Die Schlüsselnummer ist die Größe des statischen Speicherbereichs. Sobald es größer als der gesamte Swap-Bereich ist, wird es schnell wieder kompiliert. Zum Beispiel habe ich 5,3 GB Swap-Speicherplatz. Wenn Sie also GIANT_SIZE auf (1000 * M) ändern, erhalten Sie die folgende Zeit:

[[email protected]]$ time gcc HugeTest.c 

real    0m0.087s
user    0m0.026s
sys 0m0.027s

Ah, das ist eher so! Um mich (und sich selbst, wenn Sie dies zu Hause ausprobieren) davon zu überzeugen, dass der Swap-Speicher tatsächlich die magische Zahl ist, habe ich versucht, den verfügbaren Swap-Speicher auf 19 GB zu ändern und erneut die (1000 * M) -Version zu kompilieren :

[[email protected]]$ ls -ALi /extraswap 
5986 -rw-r--r-- 1 root root 14680064000 Jul 26 15:01 /extraswap
[[email protected]]$ Sudo swapon /extraswap 
[[email protected]]$ time gcc HugeTest.c 

real    4m28.089s
user    0m0.016s
sys 0m0.010s

Es war noch nicht einmal nach 4,5 Minuten fertig!

Es ist klar, dass der Linker hier etwas falsch macht, aber ich weiß nicht, wie ich das umgehen soll, außer das Programm neu zu schreiben oder mit Swap Space herumzuspielen. Ich würde gerne wissen, ob es eine Lösung gibt oder ob ich auf einen arkanen Bug gestoßen bin.

Übrigens, alle Programme werden unabhängig vom Swap-Geschäft korrekt kompiliert und ausgeführt.

Als Referenz finden Sie hier einige möglicherweise relevante Informationen:

[]$ 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: Wenn der (große) statische Speicher eines c-Programms geringfügig kleiner ist als der verfügbare Auslagerungsspeicher, braucht der Linker für die Verknüpfung des Programms eine Ewigkeit. Es ist jedoch ziemlich bissig, wenn der statische Speicherplatz geringfügig größer als der verfügbare Swap-Speicherplatz ist. Was ist damit!?

41
Rooke

Ich kann dies auf einem Ubuntu 10.10-System reproduzieren (GNU ld (GNU Binutils for Ubuntu) 2.20.51-system.20100908), und ich glaube, ich habe Ihre Antwort. Zunächst einige Methoden.

Nachdem ich bestätigt hatte, dass mir dies in einem kleinen VM passiert (512 MB RAM, 2 GB Swap), entschied ich, dass es am einfachsten ist, gcc zu stracen und zu sehen, was genau passiert, wenn alles zur Hölle geht :

~# strace -f gcc swap.c

Es beleuchtete die folgenden:

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, ".\[email protected]\0\0\0\0\0>\[email protected]\0\0\0\0\0N\[email protected]\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>

Wie wir vielleicht vermutet haben, sieht es so aus, als würde ldtatsächlich anonym versuchen, mmapden gesamten statischen Speicherbereich dieses Arrays (oder möglicherweise das gesamte Programm, es ist schwer zu sagen, da der Rest des Programms so klein ist, es könnte alles in diese zusätzliche 4096 passen).

Das ist alles schön und gut, aber warum funktioniert es, wenn wir den verfügbaren Swap auf dem System überschreiten? Lassen Sie uns swapoffdrehen und strace -f erneut ausführen ...

[pid  3618] lseek(13, 4096, SEEK_SET)   = 4096
[pid  3618] read(13, ".\[email protected]\0\0\0\0\0>\[email protected]\0\0\0\0\0N\[email protected]\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

...

Es ist nicht verwunderlich, dass ld anscheinend das Gleiche tut wie beim letzten Mal, um den gesamten Raum abzubilden. aber das System kann das nicht mehr, es scheitert! ld versucht es noch einmal und es schlägt erneut fehl, dann macht ld etwas Unerwartetes ... es geht weiter mit weniger Speicher.

Seltsam, ich denke, wir sollten uns den ldcode genauer ansehen. Drat, es gibt keine expliziten mmapname__. Dies muss aus einem einfachen alten mallocstammen. Wir müssen ld mit einigen Debug-Symbolen erstellen, um dies aufzuspüren. Als ich bin-utils 2.21.1 gebaut habe, ist das Problem leider verschwunden. Vielleicht wurde es in neueren Versionen von bin-utils behoben?

24
SoapBox

Ich habe meine OpenSuse 11.4 gefoltert und getestet (für 12.1 in einer Woche)

Ich habe 4GiB RAM + 2GiB Swap und habe keine ernsthafte Verlangsamung bemerkt, das System könnte manchmal kaputt gehen, aber trotzdem war die Kompilierungszeit kurz.

Das längste war 6 Sekunden beim starken Tauschen.

[[email protected] ~]$ free -m
             total       used       free     shared    buffers     cached
Mem:          3456       3426         30          0          4        249
-/+ buffers/cache:       3172        284
Swap:         2055       1382        672
[[email protected] ~]$ 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
[[email protected] ~]$ free -m
             total       used       free     shared    buffers     cached
Mem:          3456       3389         67          0          5        289
-/+ buffers/cache:       3094        362
Swap:         2055       1455        599
[[email protected] ~]$ free -m
             total       used       free     shared    buffers     cached
Mem:          3456       3373         82          0          4        264
-/+ buffers/cache:       3104        352
Swap:         2055       1442        612
[[email protected] ~]$ 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
[[email protected] ~]$ 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
[[email protected] ~]$ free -m
             total       used       free     shared    buffers     cached
Mem:          3456       3376         79          0          4        252
-/+ buffers/cache:       3119        336
Swap:         2055       1436        618
[[email protected] ~]$ 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

Zwischen dem Laufen habe ich Virtualbox Box VMs, Eclipse, große PDF-Dateien, mi Firefox allein mit 800+ MiB geladen und entladen. Ich ging nicht ans Limit, sonst würden viele Apps vom Betriebssystem getötet. Es hat eine Vorliebe für Firefox zu töten .. :-)

Ich ging auch auf die äußerste Definition:

#define M 1048576
#define GIANT_SIZE (20000*M)

und auch dann ändert sich nichts wesentlich.

[[email protected] ~]$ 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

Edit: Ich habe Fedora16 auf einem VM mit 512MiB RAM und 1,5GiB Swap erneut getestet, und die Dinge waren ähnlich, bis auf eine Fehlermeldung bei meiner "Maximum Stress Version" mit 20000 Megabyte wurden dem Array zugewiesen. Der Fehler besagt, dass die Arraygröße negativ war.

[[email protected] ~]$ 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

Die gleiche Reaktion erfolgt in opensuse 12.1 VM. Die Fedora 16-Installation war sehr langsam und speicherhungrig (während der Installation musste ich 800 MB im Vergleich zu OpenSuse 512 MB verwenden). Auf Fedora konnte ich Swapoff nicht verwenden, da es viel Swap-Speicherplatz beanspruchte. Ich hatte weder Probleme mit der Trägheit noch mit dem Speicher unter OpenSuse 12.1 und. Beide haben im Wesentlichen die gleichen Versionen von Kernel, gcc usw. Beide verwenden Standardinstallationen mit KDE als Desktop-Umgebung

Ich konnte Sie Probleme nicht reproduzieren, Vielleicht ist ein GCC-Problem. Versuchen Sie, eine ältere Version wie 4.5 herunterzuladen, und sehen Sie, was passiert

1
RedComet

Ich beobachte dieses Verhalten nicht (mit Debian/Sid/AMD64 auf einem 8-GB-Desktop, gcc 4.6.2, binutils gold ld (GNU Binutils für Debian 2.22) 1.11). Hier ist das geänderte Programm (Anzeige der Speicherbelegung mit 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);
}

Hier ist seine Zusammenstellung:

% 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

und seine Ausführung:

  % 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

Ich glaube, dass die Installation eines aktuellen GCC (z. B. eines GCC 4.6 ) mit einem binutils Gold-Linker für solche Programme von Bedeutung ist.

Ich höre kein Tauschen.