43 votes

Les performances de l'éditeur de liens liées à l'espace de permutation?

Voici une énigme pour votre ringard plaisir. Il est parfois pratique de se moquer de quelque chose avec un petit programme C qui utilise une grande partie de l' la mémoire statique. Pendant la programmation d'un ces de ces programmes, j'ai remarqué après le changement de Fedora 15 le programme a pris un long temps de compiler. Nous parlons des années 30 contre 0,1 s. Encore plus bizarre était que ld (la l'éditeur de liens) a été maxing sur le CPU et lentement commencé à en manger tous disponibles de la mémoire. Après quelques jouer du violon, dont les détails que j'oublie, j'ai réussi de trouver une corrélation entre ce nouveau problème et la taille de mon swap fichier. Voici un exemple de programme pour les fins de cette discussion:

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

Ce programme a un tableau géant qui a déclaré une taille d'environ 200*8 MO = 1,6 GO de mémoire statique. La compilation de ce programme prend une quantité excessive de temps:

[me@bleh]$ time gcc HugeTest.c 

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

[me@bleh]$

13s Pour un ~13 ligne C programme!? Ce n'est pas juste. Le numéro de clé est la la taille de la mémoire statique de l'espace. Dès qu'il est plus grand que le total de l'espace de swap, il commence à compiler rapidement de nouveau. Par exemple, j' ont 5.3 GO d'espace d'échange, de sorte qu'un changement GIANT_SIZE à (1000*M) donne le de temps suivantes:

[me@bleh]$ time gcc HugeTest.c 

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

Ah, c'est mieux comme ça! Pour convaincre les autres de moi-même (et vous-même, si vous êtes à essayer ceci à la maison) que l'espace d'échange est en effet la magie numéro, j'ai essayé de changer la disposition de l'espace de swap pour un vraiment énorme 19FR et d'essayer de compiler l' (1000*M) version:

[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

Il n'a même pas terminée au bout de 4,5 minutes! (Gardez à l'esprit, chers lecteurs, J'ai dû faire le sacrifice de tout ce temps à l'exécution de cette commande, comme ld commence à manger toute la mémoire disponible, puis commence l'échange, effectivement le gel de l'ordinateur).

Clairement l'éditeur de liens est en train de faire quelque chose de mal ici, mais je ne sais pas comment pour contourner ce d'autre que de la réécriture du programme ou de déconner avec l'espace de swap. J'aimerais savoir si il y a une solution, ou si j'ai suis tombé sur quelques arcanes de bug.

Par ailleurs, les programmes de toutes les compiler et de l'exécuter correctement, indépendante de toute l'activité de swap.

Pour référence, voici quelques éventuellement les informations pertinentes:

[]$ 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: Lorsque le (grand) statique de la mémoire d'un programme c est légèrement inférieure à la disposition de l'espace de swap, l'éditeur de liens prend une éternité à lier le programme. Cependant, il est assez accrocheur lorsque l'espace statique est légèrement plus grande que la disposition de l'espace de swap. Qu'est-ce que!?

25voto

SoapBox Points 14183

Je suis capable de reproduire cela sur une Ubuntu 10.10 système (GNU ld (GNU Binutils for Ubuntu) 2.20.51-system.20100908), et je pense que j'ai votre réponse. Tout d'abord, certains méthodologie.

Après confirmation de ce qui m'arrive dans une petite VM (512 mo de ram, 2 go de swap), à partir de là j'ai décidé que la meilleure chose à faire serait de strace de gcc et de voir exactement ce qui se passait lorsque tout est allé à l'enfer:

~# strace -f gcc swap.c

Il a éclairé le suivant:

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>

Il semblerait que, comme on aurait pu le croire, il ressemble ld est en réalité de manière anonyme mmap de la totalité de la mémoire statique de l'espace de ce tableau (ou éventuellement l'ensemble du programme, c'est difficile à dire, car le reste du programme est si petit qu'il peut tenir dans de ce surplus de 4096).

Donc, c'est bien beau tout ça, mais pourquoi ça marche quand on dépasse le swap sur le système? Venons-en swapoff et exécutez strace -f nouveau...

[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

...

Sans surprise, ld semble faire la même chose, il a essayé la dernière fois, à mmap l'ensemble de l'espace. mais le système n'est plus en mesure de le faire, il ne parvient pas! ld essaie de nouveau, et il échoue à nouveau, puis ld fait quelque chose d'inattendu... il se déplace avec moins de mémoire.

Bizarre, je suppose que nous ferions mieux d'avoir un coup d'oeil à l' ld code ensuite. Drat, il ne fait pas explicitement mmap. Cela doit venir de l'intérieur de la plaine de vieux - malloc. Nous aurons à construire ld avec certains symboles de débogage pour suivre tout cela. Malheureusement, quand j'ai construit bin-utils 2.21.1 le problème a disparu. Perhap il a été corrigé dans les versions plus récentes de bin-utils?

1voto

RedComet Points 884

J'ai torturé testé mon OpenSuse 11.4 (passant de 12,1 en une semaine)

J'ai 4GiB de ram + 2GiB swap et n'a pas remarqué de graves ralentir, le système risque de bousiller à la fois, mais toujours le temps de compilation a été courte.

Le plus long a été de 6 secondes alors que le heavy de permutation.

[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

Entre l'exécution j'ai chargé et déchargé Virtualbox Boîte de VM, Eclipse, des fichiers pdf volumineux, mi firefox seulement à l'aide d'800+ MiB. Je didi ne pas aller à la limite, sinon, de nombreuses Applications seront tués par l'OS. Il a une préférence pour le meurtre de Firefox.. :-)

Je suis aussi allé à l'extrême de la définition:

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

et même alors, rien de changer de façon significative.

[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

Edit: J'ai re-testé à l'aide de Fedora16 sur une machine virtuelle avec 512MiB de RAM et 1,5 Go de swap, et les choses sont les mêmes, sauf pour un message d'erreur sur mon "stress maximum version" où 20000 mégaoctets ont été affectés à la matrice. L'erreur de dire que la taille de la matrice a été négative.

[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

La même réaction se produit dans opensuse 12.1 VM. Fedora 16 installer cousue, très lent et gourmands en mémoire(lors de l'installation j'ai eu à utiliser 800MiB contre OpenSuse 512 MiB), je ne pouvais pas utiliser swapoff sur Fedora parce que c'était avec beaucoup d'espace de swap. Je n'avais pas de lenteur, ni de problèmes de mémoire sur OpenSuse 12.1 et . Les deux ont essentiellement la même version de noyau, gcc, etc. À la fois à l'aide de stock avec KDE comme environnement de Bureau

Je ne pouvais pas reproduire vous avez des questions, Peut-être est gcc question connexe. Essayer de télécharger une version plus ancienne comme le 4,5 et voir ce qui se passe

1voto

Basile Starynkevitch Points 67055

Je ne suis pas d'observer ce comportement (avec Debian/Sid/AMD64 sur un 8 go de bureau, gcc 4.6.2, binutils or ld (GNU Binutils pour Debian 2.22) 1.11). Voici le programme (l'affichage de sa carte mémoire avec 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);
}

Voici la compilation:

% 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

et de son exécution:

  % 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

Je crois que l'installation d'une version récente de GCC (par exemple, un GCC 4.6) avec un binutils Or de l'éditeur de liens est importante pour de tels programmes.

Je n'ai pas entendu toute permutation impliqués.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X