108 votes

Comment corriger l'erreur de compilation de GCC lors de la compilation de >2 Go de code ?

J'ai un grand nombre de fonctions totalisant environ 2,8 Go de code objet (malheureusement, il n'y a pas moyen de contourner ce problème, l'informatique scientifique...).

Lorsque j'essaie de les lier, j'obtiens (attendu) relocation truncated to fit: R_X86_64_32S que j'espérais contourner en spécifiant l'indicateur du compilateur -mcmodel=medium . Toutes les bibliothèques qui sont liées en plus et dont j'ai le contrôle sont compilées avec la commande -fpic drapeau.

Pourtant, l'erreur persiste, et je suppose que certaines bibliothèques auxquelles je me lie ne sont pas compilées avec PIC.

Voici l'erreur :

/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crt1.o: In function `_start':
(.text+0x12): relocation truncated to fit: R_X86_64_32S against symbol `__libc_csu_fini'     defined in .text section in /usr/lib64/libc_nonshared.a(elf-init.oS)
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crt1.o: In function `_start':
(.text+0x19): relocation truncated to fit: R_X86_64_32S against symbol `__libc_csu_init'    defined in .text section in /usr/lib64/libc_nonshared.a(elf-init.oS)
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crti.o: In function    `call_gmon_start':
(.text+0x7): relocation truncated to fit: R_X86_64_GOTPCREL against undefined symbol      `__gmon_start__'
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtbegin.o: In function `__do_global_dtors_aux':
crtstuff.c:(.text+0xb): relocation truncated to fit: R_X86_64_PC32 against `.bss' 
crtstuff.c:(.text+0x13): relocation truncated to fit: R_X86_64_32 against symbol `__DTOR_END__' defined in .dtors section in /usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtend.o
crtstuff.c:(.text+0x19): relocation truncated to fit: R_X86_64_32S against `.dtors'
crtstuff.c:(.text+0x28): relocation truncated to fit: R_X86_64_PC32 against `.bss'
crtstuff.c:(.text+0x38): relocation truncated to fit: R_X86_64_PC32 against `.bss'
crtstuff.c:(.text+0x3f): relocation truncated to fit: R_X86_64_32S against `.dtors'
crtstuff.c:(.text+0x46): relocation truncated to fit: R_X86_64_PC32 against `.bss'
crtstuff.c:(.text+0x51): additional relocation overflows omitted from the output
collect2: ld returned 1 exit status
make: *** [testsme] Error 1

Et les bibliothèques du système que je relie :

-lgfortran -lm -lrt -lpthread

Avez-vous une idée de l'endroit où il faut chercher le problème ?

EDIT :

Tout d'abord, merci pour la discussion...

Pour clarifier un peu, j'ai des centaines de fonctions (d'une taille d'environ 1 Mo chacune dans des fichiers d'objets séparés) comme celle-ci :

double func1(std::tr1::unordered_map<int, double> & csc, 
             std::vector<EvaluationNode::Ptr> & ti, 
             ProcessVars & s)
{
    double sum, prefactor, expr;

    prefactor = +s.ds8*s.ds10*ti[0]->value();
    expr =       ( - 5/243.*(s.x14*s.x15*csc[49300] + 9/10.*s.x14*s.x15*csc[49301] +
           1/10.*s.x14*s.x15*csc[49302] - 3/5.*s.x14*s.x15*csc[49303] -
           27/10.*s.x14*s.x15*csc[49304] + 12/5.*s.x14*s.x15*csc[49305] -
           3/10.*s.x14*s.x15*csc[49306] - 4/5.*s.x14*s.x15*csc[49307] +
           21/10.*s.x14*s.x15*csc[49308] + 1/10.*s.x14*s.x15*csc[49309] -
           s.x14*s.x15*csc[51370] - 9/10.*s.x14*s.x15*csc[51371] -
           1/10.*s.x14*s.x15*csc[51372] + 3/5.*s.x14*s.x15*csc[51373] +
           27/10.*s.x14*s.x15*csc[51374] - 12/5.*s.x14*s.x15*csc[51375] +
           3/10.*s.x14*s.x15*csc[51376] + 4/5.*s.x14*s.x15*csc[51377] -
           21/10.*s.x14*s.x15*csc[51378] - 1/10.*s.x14*s.x15*csc[51379] -
           2*s.x14*s.x15*csc[55100] - 9/5.*s.x14*s.x15*csc[55101] -
           1/5.*s.x14*s.x15*csc[55102] + 6/5.*s.x14*s.x15*csc[55103] +
           27/5.*s.x14*s.x15*csc[55104] - 24/5.*s.x14*s.x15*csc[55105] +
           3/5.*s.x14*s.x15*csc[55106] + 8/5.*s.x14*s.x15*csc[55107] -
           21/5.*s.x14*s.x15*csc[55108] - 1/5.*s.x14*s.x15*csc[55109] -
           2*s.x14*s.x15*csc[55170] - 9/5.*s.x14*s.x15*csc[55171] -
           1/5.*s.x14*s.x15*csc[55172] + 6/5.*s.x14*s.x15*csc[55173] +
           27/5.*s.x14*s.x15*csc[55174] - 24/5.*s.x14*s.x15*csc[55175] +
           // ...
           ;

        sum += prefactor*expr;
    // ...
    return sum;
}

L'objet s est relativement faible et conserve les constantes nécessaires x14, x15, ..., ds0, ..., etc. alors que ti renvoie simplement un double provenant d'une bibliothèque externe. Comme vous pouvez le voir, csc[] est une carte de valeurs précalculée qui est également évaluée dans des fichiers objets séparés (à nouveau des centaines avec une taille d'environ 1 Mo chacun) de la forme suivante :

void cscs132(std::tr1::unordered_map<int,double> & csc, ProcessVars & s)
{
    {
    double csc19295 =       + s.ds0*s.ds1*s.ds2 * ( -
           32*s.x12pow2*s.x15*s.x34*s.mbpow2*s.mWpowinv2 -
           32*s.x12pow2*s.x15*s.x35*s.mbpow2*s.mWpowinv2 -
           32*s.x12pow2*s.x15*s.x35*s.x45*s.mWpowinv2 -
           32*s.x12pow2*s.x25*s.x34*s.mbpow2*s.mWpowinv2 -
           32*s.x12pow2*s.x25*s.x35*s.mbpow2*s.mWpowinv2 -
           32*s.x12pow2*s.x25*s.x35*s.x45*s.mWpowinv2 +
           32*s.x12pow2*s.x34*s.mbpow4*s.mWpowinv2 +
           32*s.x12pow2*s.x34*s.x35*s.mbpow2*s.mWpowinv2 +
           32*s.x12pow2*s.x34*s.x45*s.mbpow2*s.mWpowinv2 +
           32*s.x12pow2*s.x35*s.mbpow4*s.mWpowinv2 +
           32*s.x12pow2*s.x35pow2*s.mbpow2*s.mWpowinv2 +
           32*s.x12pow2*s.x35pow2*s.x45*s.mWpowinv2 +
           64*s.x12pow2*s.x35*s.x45*s.mbpow2*s.mWpowinv2 +
           32*s.x12pow2*s.x35*s.x45pow2*s.mWpowinv2 -
           64*s.x12*s.p1p3*s.x15*s.mbpow4*s.mWpowinv2 +
           64*s.x12*s.p1p3*s.x15pow2*s.mbpow2*s.mWpowinv2 +
           96*s.x12*s.p1p3*s.x15*s.x25*s.mbpow2*s.mWpowinv2 -
           64*s.x12*s.p1p3*s.x15*s.x35*s.mbpow2*s.mWpowinv2 -
           64*s.x12*s.p1p3*s.x15*s.x45*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.p1p3*s.x25*s.mbpow4*s.mWpowinv2 +
           32*s.x12*s.p1p3*s.x25pow2*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.p1p3*s.x25*s.x35*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.p1p3*s.x25*s.x45*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.p1p3*s.x45*s.mbpow2 +
           64*s.x12*s.x14*s.x15pow2*s.x35*s.mWpowinv2 +
           96*s.x12*s.x14*s.x15*s.x25*s.x35*s.mWpowinv2 +
           32*s.x12*s.x14*s.x15*s.x34*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.x14*s.x15*s.x35*s.mbpow2*s.mWpowinv2 -
           64*s.x12*s.x14*s.x15*s.x35pow2*s.mWpowinv2 -
           32*s.x12*s.x14*s.x15*s.x35*s.x45*s.mWpowinv2 +
           32*s.x12*s.x14*s.x25pow2*s.x35*s.mWpowinv2 +
           32*s.x12*s.x14*s.x25*s.x34*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.x14*s.x25*s.x35pow2*s.mWpowinv2 -
           // ...

       csc.insert(cscMap::value_type(192953, csc19295));
    }

    {
       double csc19296 =      // ... ;

       csc.insert(cscMap::value_type(192956, csc19296));
    }

    // ...
}

C'est à peu près tout. L'étape finale consiste alors simplement à appeler tous ces func[i] et en résumant le résultat.

Concernant le fait que c'est un cas plutôt spécial et inhabituel : Oui, il l'est. C'est ce à quoi les gens doivent faire face lorsqu'ils essaient de faire des calculs de haute précision pour la physique des particules.

EDIT2 :

Je dois également ajouter que x12, x13, etc. ne sont pas vraiment des constantes. Elles sont fixées à des valeurs spécifiques, toutes ces fonctions sont exécutées et le résultat est renvoyé, puis un nouvel ensemble de x12, x13, etc. est choisi pour produire la valeur suivante. Et cela doit être fait 10 5 à 10 6 temps...

EDIT3 :

Merci pour les suggestions et la discussion jusqu'à présent... Je vais essayer d'enrouler les boucles lors de la génération du code d'une manière ou d'une autre, je ne sais pas exactement comment le faire, pour être honnête, mais c'est la meilleure solution.

BTW, je n'ai pas essayé de me cacher derrière le fait que "c'est du calcul scientifique - pas moyen d'optimiser".
C'est juste que la base de ce code est quelque chose qui sort d'une "boîte noire" à laquelle je n'ai pas vraiment accès et, de plus, tout cela fonctionnait très bien avec des exemples simples, et je me sens surtout dépassé par ce qui se passe dans une application du monde réel...

EDIT4 :

J'ai donc réussi à réduire la taille du code de l'application csc d'environ un quart en simplifiant les expressions dans un système de calcul formel ( Mathematica ). Je vois maintenant aussi un moyen de le réduire d'un autre ordre de grandeur en appliquant d'autres astuces avant de générer le code (ce qui ramènerait cette partie à environ 100 Mo) et j'espère que cette idée fonctionnera.

Maintenant, en rapport avec vos réponses :

J'essaie d'enrouler les boucles à nouveau dans le func où un CAS ne sera pas d'une grande aide, mais j'ai déjà quelques idées. Par exemple, trier les expressions par les variables comme x12, x13,... , analyser le csc avec Python et générer des tableaux qui les relient les uns aux autres. Ensuite, je peux au moins générer ces parties sous forme de boucles. Comme cela semble être la meilleure solution jusqu'à présent, je marque ceci comme la meilleure réponse.

Cependant, j'aimerais aussi donner du crédit à VJo. GCC 4.6 fonctionne en effet beaucoup meilleur, produit un code plus petit et est plus rapide. L'utilisation du grand modèle permet de travailler sur le code tel quel. Donc, techniquement, c'est la bonne réponse, mais changer l'ensemble du concept est une bien meilleure approche.

Merci à tous pour vos suggestions et votre aide. Si cela intéresse quelqu'un, je vais poster le résultat final dès que je serai prêt.

REMARQUES :

Juste quelques remarques sur d'autres réponses : Le code que j'essaie d'exécuter ne provient pas d'une expansion de fonctions/algorithmes simples et d'un stupide déroulage inutile. Ce qui se passe en fait, c'est que le matériel avec lequel nous commençons est constitué d'objets mathématiques assez compliqués et que nous les amenons à un niveau numériquement plus élevé. Calculable génère ces expressions. Le problème réside en fait dans la théorie physique sous-jacente. La complexité des expressions intermédiaires s'échelonne de façon factorielle, ce qui est bien connu, mais lorsqu'on combine tout cela à quelque chose de physiquement mesurable - un observable - cela se résume à une poignée de très petites fonctions qui forment la base des expressions. (Il y a définitivement quelque chose de "faux" à cet égard avec la théorie générale et la théorie de l'infini. uniquement disponible sur ansatz Nous essayons d'amener cet ansatz à un autre niveau, qui n'est plus faisable analytiquement et où la base des fonctions nécessaires n'est pas connue. Nous essayons donc de le forcer brutalement comme ceci. Ce n'est pas la meilleure méthode, mais nous espérons qu'elle nous aidera à comprendre la physique en question...

DERNIÈRE MODIFICATION :

Grâce à toutes vos suggestions, j'ai réussi à réduire considérablement la taille du code, en utilisant Mathematica et une modification du générateur de code pour l'outil d'analyse de l'environnement. func qui va dans le sens de la réponse ci-dessus :)

J'ai simplifié le csc avec Mathematica, ce qui le ramène à 92 Mo. C'est la partie irréductible. Les premières tentatives ont pris une éternité, mais après quelques optimisations, le programme s'exécute maintenant en 10 minutes environ sur un seul processeur.

L'effet sur le func a été spectaculaire : La taille totale du code est tombée à environ 9 Mo, et le code total est maintenant de l'ordre de 100 Mo. Il est maintenant logique d'activer les optimisations et l'exécution est assez rapide.

Encore une fois, merci à tous pour vos suggestions, j'ai beaucoup appris.

1voto

Jed Points 900

Cela ressemble au résultat d'une génération de code qui a mal tourné, peut-être par algèbre symbolique et/ou déroulage manuel. Les manipulations symboliques sont bien connues pour croître exponentiellement avec la profondeur de l'arbre d'expression ou du graphe de calcul. Il est probable que la différenciation automatique puisse être utilisée ici, ce qui rendrait la taille du code assez petite et accélérerait aussi l'exécution de façon spectaculaire.

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