GCC-Kompilierungsfehler mit> 2 GB Code


108

Ich habe eine große Anzahl von Funktionen mit insgesamt rund 2,8 GB Objektcode (leider führt kein Weg daran vorbei, wissenschaftliches Rechnen ...)

Wenn ich versuche, sie zu verknüpfen, erhalte ich (erwartete) relocation truncated to fit: R_X86_64_32SFehler, die ich durch Angabe des Compiler-Flags umgehen wollte -mcmodel=medium. Alle zusätzlich verknüpften Bibliotheken, auf die ich Einfluss habe, werden mit dem -fpicFlag kompiliert .

Trotzdem bleibt der Fehler bestehen und ich gehe davon aus, dass einige Bibliotheken, mit denen ich verknüpfe, nicht mit PIC kompiliert wurden.

Hier ist der Fehler:

/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

Und Systembibliotheken, gegen die ich verlinke:

-lgfortran -lm -lrt -lpthread

Gibt es Hinweise, wo Sie nach dem Problem suchen können?

EDIT: Zunächst einmal vielen Dank für die Diskussion ... Zur Verdeutlichung habe ich Hunderte von Funktionen (jede ca. 1 MB groß in separaten Objektdateien) wie folgt:

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

Das Objekt sist relativ klein und behält die erforderlichen Konstanten x14, x15, ..., ds0, ... usw. bei, während tinur ein Double aus einer externen Bibliothek zurückgegeben wird. Wie Sie sehen können, csc[]handelt es sich um eine vorberechnete Wertekarte, die auch in separaten Objektdateien (wiederum Hunderte mit jeweils ca. 1 MB Größe) der folgenden Form ausgewertet wird:

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

    // ...
}

Das ist alles. Der letzte Schritt besteht dann nur darin, alle diese aufzurufen func[i]und das Ergebnis zusammenzufassen.

In Bezug auf die Tatsache, dass dies ein ganz besonderer und ungewöhnlicher Fall ist: Ja, das ist es. Damit müssen sich die Menschen auseinandersetzen, wenn sie versuchen, hochpräzise Berechnungen für die Teilchenphysik durchzuführen.

EDIT2: Ich sollte auch hinzufügen, dass x12, x13 usw. keine wirklichen Konstanten sind. Sie werden auf bestimmte Werte gesetzt, alle diese Funktionen werden ausgeführt und das Ergebnis zurückgegeben. Anschließend wird ein neuer Satz von x12, x13 usw. ausgewählt, um den nächsten Wert zu erzeugen. Und das muss 10 ^ 5 bis 10 ^ 6 mal gemacht werden ...

EDIT3: Vielen Dank für die Vorschläge und die bisherige Diskussion ... Ich werde versuchen, die Schleifen bei der Codegenerierung irgendwie aufzurollen, nicht sicher, wie genau das geht, um ehrlich zu sein, aber das ist die beste Wahl.

Übrigens habe ich nicht versucht, mich dahinter zu verstecken "das ist wissenschaftliches Rechnen - keine Möglichkeit zur Optimierung". Es ist nur so, dass die Basis für diesen Code etwas ist, das aus einer "Black Box" stammt, auf die ich keinen wirklichen Zugriff habe, und außerdem hat das Ganze mit einfachen Beispielen großartig funktioniert, und ich fühle mich hauptsächlich überwältigt von dem, was in einem realen passiert Weltanwendung ...

EDIT4: Ich habe es also geschafft, die Codegröße der cscDefinitionen durch Vereinfachung der Ausdrücke in einem Computeralgebrasystem ( Mathematica ) um etwa ein Viertel zu reduzieren . Ich sehe jetzt auch eine Möglichkeit, es um eine weitere Größenordnung oder so zu reduzieren, indem ich einige andere Tricks anwende, bevor ich den Code generiere (was diesen Teil auf ungefähr 100 MB reduzieren würde), und ich hoffe, dass diese Idee funktioniert.

Nun zu Ihren Antworten: Ich versuche, die Schleifen im funcs wieder aufzurollen , wo ein CAS nicht viel hilft, aber ich habe bereits einige Ideen. Wenn Sie beispielsweise die Ausdrücke nach Variablen wie sortieren x12, x13,..., analysieren Sie die cscs mit Python und generieren Sie Tabellen, die sie miteinander in Beziehung setzen. Dann kann ich diese Teile zumindest als Schleifen erzeugen. Da dies bisher die beste Lösung zu sein scheint, bezeichne ich dies als die beste Antwort.

Ich möchte jedoch auch VJo Ehre machen. GCC 4.6 funktioniert in der Tat viel besser, produziert kleineren Code und ist schneller. Die Verwendung des großen Modells funktioniert mit dem Code wie er ist. Technisch gesehen ist dies die richtige Antwort, aber eine Änderung des gesamten Konzepts ist ein viel besserer Ansatz.

Vielen Dank für Ihre Vorschläge und Hilfe. Wenn jemand interessiert ist, werde ich das endgültige Ergebnis veröffentlichen, sobald ich bereit bin.

BEMERKUNGEN: Nur einige Anmerkungen zu einigen anderen Antworten: Der Code, den ich ausführen möchte, stammt nicht aus einer Erweiterung einfacher Funktionen / Algorithmen und dummem unnötigem Abrollen. Was tatsächlich passiert, ist, dass das, womit wir beginnen, ziemlich komplizierte mathematische Objekte sind und diese Ausdrücke in eine numerisch berechenbare Form bringen. Das Problem liegt tatsächlich in der zugrunde liegenden physikalischen Theorie. Die Komplexität von Zwischenausdrücken skaliert faktoriell, was bekannt ist, aber wenn all diese Dinge zu etwas physikalisch Messbarem kombiniert werden - einem Beobachtbaren -, läuft es nur auf eine Handvoll sehr kleiner Funktionen hinaus, die die Grundlage der Ausdrücke bilden. (Es gibt definitiv etwas "Falsches" in dieser Hinsicht mit dem Allgemeinen und nur verfügbarAnsatz, der als "Störungstheorie" bezeichnet wird) Wir versuchen, diesen Ansatz auf eine andere Ebene zu bringen, die analytisch nicht mehr realisierbar ist und bei der die Grundlage der benötigten Funktionen nicht bekannt ist. Also versuchen wir es so brutal zu erzwingen. Nicht der beste Weg, aber hoffentlich einer, der am Ende zu unserem Verständnis der vorliegenden Physik beiträgt ...

LAST EDIT: Dank all Ihrer Vorschläge habe ich es geschafft, die Codegröße erheblich zu reduzieren, indem ich Mathematica und eine Modifikation des Codegenerators für das funcs etwas in Anlehnung an die Top-Antwort verwendet habe :)

Ich habe die cscFunktionen mit Mathematica vereinfacht und auf 92 MB reduziert. Dies ist der irreduzible Teil. Die ersten Versuche haben ewig gedauert, aber nach einigen Optimierungen dauert dies nun in etwa 10 Minuten auf einer einzelnen CPU.

Die Auswirkung auf das funcs war dramatisch: Die gesamte Codegröße für sie beträgt ungefähr 9 MB, sodass der Code jetzt im Bereich von 100 MB liegt. Jetzt ist es sinnvoll, Optimierungen zu aktivieren und die Ausführung ist recht schnell.

Nochmals vielen Dank für Ihre Vorschläge, ich habe viel gelernt.


17
Wenn Sie so viele Daten haben, sollten Sie diese mmapzur Laufzeit aus den Quelldateien und stattdessen selbst aus einer externen Binärdatei verschieben.
R .. GitHub STOP HELPING ICE

3
Können Sie ein Beispiel für eine (oder zwei) dieser Funktionen geben? Das sieht wirklich seltsam aus. Sie können diese Funktionen auch dynamisch mit der Funktion dl * laden.
Patrick Schlüter

7
@bbtrb: Mein erster Instinkt ähnelt dem von R .., es klingt wie ein Designproblem. Zugegeben, ich weiß nicht, was in wissenschaftlichen Computerkreisen üblich ist, aber ich habe noch nie von jemandem gehört, der versucht, eine 2,8-GB-Objektdatei oder etwas in der Nähe davon zu verknüpfen, und ich bin nicht sicher, ob GCC dies wirklich unterstützen würde. Ehrlich gesagt würde ich erwarten, dass jeder Code-Blob dieser Größe reine Spaghetti ist.
Nicholas Knight

46
Es gibt absolut keine Möglichkeit, dass die optimale Lösung für das Problem 2 GB Objektdatei umfasst.
David Heffernan

35
Geben Sie Ihre Daten nicht in den Code ein
David Heffernan

Antworten:


53

Sie haben also bereits ein Programm, das diesen Text erzeugt:

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] -...

und

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 -...

richtig?

Wenn alle Ihre Funktionen ein ähnliches "Format" haben (n Zahlen m mal multiplizieren und die Ergebnisse hinzufügen - oder etwas Ähnliches), dann können Sie dies meiner Meinung nach tun:

  • Ändern Sie das Generatorprogramm so, dass Offsets anstelle von Strings ausgegeben werden (dh anstelle des Strings "s.ds0", den es erzeugt offsetof(ProcessVars, ds0)
  • Erstellen Sie ein Array solcher Offsets
  • Schreiben Sie einen Evaluator, der das obige Array und die Basisadressen der Strukturzeiger akzeptiert und ein Ergebnis erzeugt

Der Array + Evaluator repräsentiert dieselbe Logik wie eine Ihrer Funktionen, aber nur der Evaluator ist Code. Das Array ist "Daten" und kann entweder zur Laufzeit generiert oder auf der Festplatte gespeichert und i Chunks oder mit einer Speicherzuordnungsdatei gelesen werden.

Für Ihr spezielles Beispiel in func1 vorstellen , wie Sie die Funktion über eine Evaluator umschreiben würden , wenn Sie den Zugriff auf die Basisadresse haben sund cscauch ein Vektor wie Darstellung der Konstanten und den Offsets Sie zu den Basisadressen hinzufügen müssen , um zu bekommen x14, ds8undcsc[51370]

Sie müssen eine neue Form von "Daten" erstellen, die beschreibt, wie die tatsächlichen Daten verarbeitet werden, die Sie an Ihre große Anzahl von Funktionen übergeben.


45

Das von Linux verwendete x86-64-ABI definiert ein "großes Modell" speziell zur Vermeidung solcher Größenbeschränkungen, das 64-Bit-Verschiebungstypen für GOT und PLT enthält. (Siehe die Tabelle in Abschnitt 4.4.2 und die Befehlssequenzen in 3.5.5, die zeigen, wie sie verwendet werden.)

Da Ihre Funktionen 2,8 GB belegen, haben Sie kein Glück, da gcc keine großen Modelle unterstützt. Was Sie tun können, ist, Ihren Code so zu reorganisieren, dass Sie ihn in gemeinsam genutzte Bibliotheken aufteilen können, die Sie dynamisch verknüpfen würden.

Wenn dies nicht möglich ist, wie jemand vorgeschlagen hat, können Sie Ihre Daten zur Laufzeit laden (entweder als normale Datei oder als MMAP), anstatt sie in Code zu setzen (zu kompilieren und zu verknüpfen), da sie sehr groß sind.

BEARBEITEN

Das große Modell scheint von gcc 4.6 unterstützt zu werden (siehe diese Seite ). Sie können das versuchen, aber das oben Gesagte gilt immer noch für die Reorganisation Ihres Codes.


Sie sagen also, wenn ich die Objektdateien in mehreren kleinen gemeinsam genutzten Bibliotheken gruppieren würde, würde ich die Einschränkungen überwinden?
Bbtrb

3
@bbtrb Richtig. Aber ich würde immer noch nach einer anderen Möglichkeit suchen, Ihre Funktionen zu implementieren. Ich wette, Ihre Zusammenstellung dauert ewig
BЈовић

18
WTF? Dieser Code muss von einem Skript generiert werden. niemand schreibt Megabyte Code von Hand! Dieselbe Logik, die den Code generiert, kann auch zum Ausführen der Berechnung verwendet werden.
Zvrba

6
Ich empfehle dringend, gcc 4.6 auszuprobieren. Es ist sehr wahrscheinlich, dass für dieses Programm überlegener Code als für gcc 4.1 erstellt wird. es könnte sogar die ganze Sache in 2GB quetschen können , ohne Sie etwas klug, zu tun zu haben , das Problem (try Kombinationen von -Os, -fwhole-Programm und -flto beseitigen - mit diesem Volumen von Code, für die Größe optimiert ist Geschwindigkeitsoptimierung). Wenn dies jedoch nicht ausreicht, sollten Sie sich auch darüber im Klaren sein, dass Sie mindestens einen Teil der C-Bibliothek im großen Modell (crt * .o, libc_nonshared.a) neu erstellen müssen, damit das große Modell funktioniert und libpthread_nonshared.a).
zwol

1
@bdonlan Statische Verknüpfung ist ebenfalls möglich.
Zvrba

37

Mit einem Programm dieser Seite übersteigen Cache-Fehler für Code sehr wahrscheinlich die Kosten für Schleifen zur Laufzeit. Ich würde empfehlen, dass Sie zu Ihrem Codegenerator zurückkehren und ihn eine kompakte Darstellung für das erstellen lassen, was ausgewertet werden soll (dh eine, die wahrscheinlich in den D-Cache passt), und diese dann mit einem Interpreter in Ihrem Programm ausführen. Sie können auch sehen, ob Sie kleinere Kernel herausrechnen können, die noch eine erhebliche Anzahl von Operationen ausführen, und diese dann als 'Anweisungen' im interpretierten Code verwenden.


21

Der Fehler tritt auf, weil Sie zu viel CODE haben, keine Daten! Dies wird dadurch angezeigt, dass beispielsweise __libc_csu_fini(was eine Funktion ist) darauf verwiesen wird _startund die Verschiebung entsprechend abgeschnitten wird. Dies bedeutet, dass _start(der wahre Einstiegspunkt des Programms) versucht, diese Funktion über einen SIGNIERTEN 32-Bit-Offset aufzurufen, der nur einen Bereich von 2 GB hat. Da die Gesamtmenge Ihres Objektcodes ~ 2,8 GB beträgt, werden die Fakten überprüft.

Wenn Sie Ihre Datenstrukturen neu gestalten könnten, könnte ein Großteil Ihres Codes "komprimiert" werden, indem Sie die großen Ausdrücke als einfache Schleifen umschreiben.

Sie können auch csc[]in einem anderen Programm rechnen , die Ergebnisse in einer Datei speichern und sie bei Bedarf einfach laden.


Können Sie ein Beispiel geben, wie Sie die Funktionen mit einfachen Schleifen umschreiben würden? Ich folge dir nicht genau. csc[]muss sehr oft berechnet werden und ich möchte Festplatten-E / A vermeiden.
Bbtrb

4
@bbtr: Zum Beispiel für func1oben so etwas wie : for (int i = 0; i < N; ++i) expr += constants[i].*s.x14*s.x15*csc[49300 + i];.
HighCommander4

@ HighCommander4: absolut, ich stimme zu. Es liegt knapp über meinem Kopf, wie man so etwas automatisch generiert. Vielleicht mit einem separaten Array, das die Indizes speichert ...
bbtrb

2
@bbtrb: Da gibt es keine freaking Art und Weise , dass jede genug Quelle zu produzieren 2.8GB von Objektcode von Hand, insbesondere mit solchen un-mnemonic Symbolnamen geschrieben, ein Code - Generator muss verwendet wurde. Arbeite damit.
Donal Fellows

15

Ich denke, alle sind sich einig, dass es einen anderen Weg geben sollte, um das zu tun, was Sie tun möchten. Das Kompilieren von Hunderten von Megabyte (Gigabyte?) Code, das Verknüpfen mit einer ausführbaren Datei mit einer Größe von mehreren Gigabyte und das Ausführen klingt einfach sehr ineffizient.

Wenn ich Ihr Problem richtig verstehe, verwenden Sie eine Art Codegenerator G, um eine Reihe von Funktionen zu generieren func1...N, die eine Reihe von Karten csc1...Mals Eingabe verwenden. Was Sie tun möchten, ist zu berechnen csc1...Mund eine Schleife von 1.000.000 Mal für verschiedene Eingaben und jedes Mal zu führen s = func1 + func2 + ... + funcN. Sie haben jedoch nicht angegeben, wie sie fucn1...Nzusammenhängen csc1...M.

Wenn all dies zutrifft, sollten Sie in der Lage sein, das Problem auf unterschiedliche Weise auf den Kopf zu stellen, was möglicherweise viel einfacher zu handhaben und sogar möglicherweise schneller sein kann (dh den Cache Ihres Computers tatsächlich funktionieren zu lassen).

Neben dem praktischen Problem der Größe der Objektdateien ist Ihr aktuelles Programm nicht effizient, da es den Zugriff auf die Daten nicht lokalisiert (zu viele große Karten) und keine lokalisierte Codeausführung hat (zu viele sehr lange Funktionen).

Wie wäre es, wenn Sie Ihr Programm in drei Phasen aufteilen: Phase 1 erstellen csc1...Mund speichern. Phase 2 wird einzeln erstellt func, 1.000.000 Mal mit jeder Eingabe ausgeführt und die Ergebnisse gespeichert. In Phase 3 wird die Summe der Ergebnisse der gespeicherten func1...NErgebnisse für jeden Lauf 1.000.000 Mal ermittelt. Das Gute an dieser Lösung ist, dass sie problemlos über mehrere unabhängige Maschinen hinweg parallel geschaltet werden kann.

Edit: @bbtrb, könntest du irgendwo eine func und eine csc zur Verfügung stellen? Sie scheinen sehr regelmäßig und komprimierbar zu sein. Zum Beispiel scheint func1 nur eine Summe von Ausdrücken zu sein, die jeweils aus 1 Koeffizienten, 2 Indizes für die Variablen in s und 1 Index für csc bestehen. So kann es auf eine schöne Schleife reduziert werden. Wenn Sie vollständige Beispiele zur Verfügung stellen, können Sie sicher Wege finden, diese in Schleifen anstatt in langen Ausdrücken zu komprimieren.


Ja, du verstehst das richtig :) Es gibt jedoch einige Probleme mit deinem Vorschlag: 1. Das schlechteste funcs hängt von fast allen cscs ab und diese Zahlen müssen ebenfalls 10 ^ 6 Mal berechnet werden. 2. Die Eingabe wird von einem adaptiven Monte-Carlo-Integrator erhalten, was bedeutet, dass der Integrator an jedem Punkt das vollständige Ergebnis kennen muss, um den resultierenden Fehler durch Verfeinern des Netzes in der Nähe des Punkts bei Bedarf reduzieren zu können. 3. Die großen Ausdrücke für cscpersist ...
bbtrb

1
Bedeutet das also, dass Sie nicht jede cscin jeder Iteration unabhängig von den anderen berechnen können ? Wenn sie unabhängig wären, könnten Sie sie immer noch 10 ^ 6 Mal ausführen und die Ergebnisse speichern. Wenn es jedoch Abhängigkeiten zwischen ihnen gibt, müssen Sie möglicherweise herausfinden, welche mit welchen verwandt sind, beispielsweise mit einem Abhängigkeitsdiagramm, und dann versuchen, herauszufinden, ob Sie es in mehrere unabhängige Unterdiagramme aufteilen können. Alles in allem denke ich, dass der Schlüssel darin besteht, das Problem in mehrere unabhängige Unterprobleme aufzuteilen.
AlefSin

5

Wenn ich Ihre Fehler richtig gelesen habe, ist das, was Sie dazu bringt, das Limit zu überschreiten, der initialisierte Datenabschnitt (wenn es der Code wäre, hätten Sie meiner Meinung nach weit mehr Fehler). Haben Sie große Mengen globaler Daten? In diesem Fall würde ich das Programm so umstrukturieren, dass sie dynamisch zugewiesen werden. Wenn die Daten initialisiert werden, würde ich sie aus einer Konfigurationsdatei lesen.

Übrigens:

(.text + 0x20): undefinierter Verweis auf `main '

Ich denke du hast ein anderes Problem.


1
Ja, Sie haben Recht, dummer Fehler, aber er löst die anderen Fehler nicht.
Bbtrb

3

Es sieht für mich so aus, als würde der Code eine numerische Integration mit einer Art adaptiver Tiefenmethode durchführen. Leider ist der Codegenerator (oder besser gesagt der Autor des Codegenerators) so dumm , dass er eine Funktion pro Patch und keine pro Patch- Typ generiert . Als solches wird zu viel Code produziert, um kompiliert zu werden, und selbst wenn er kompiliert werden könnte, wäre seine Ausführung schmerzhaft, da nichts jemals irgendwo geteilt wird. (Können Sie sich den Schmerz vorstellen, der dadurch entsteht, dass jede Seite des Objektcodes von der Festplatte geladen werden muss, weil nie etwas gemeinsam genutzt wird und es daher immer ein Kandidat für die Räumung des Betriebssystems ist? Ganz zu schweigen von Anweisungscaches, die nutzlos sein werden.)

Die Lösung besteht darin, nicht mehr alles abzuwickeln. Für diese Art von Code möchten Sie die Freigabe maximieren, da der Aufwand für zusätzliche Anweisungen für den Zugriff auf Daten in komplexeren Mustern ohnehin durch die Kosten für den Umgang mit dem (vermutlich) großen zugrunde liegenden Datensatz absorbiert wird. Es ist auch möglich, dass der Codegenerator dies sogar standardmäßig tut und dass der Wissenschaftler einige Optionen zum Abrollen sah (mit dem Hinweis, dass diese manchmal die Geschwindigkeit verbessern) und sie alle auf einmal einschaltete und nun darauf besteht, dass dieses resultierende Durcheinander akzeptiert wird vom Computer, anstatt die tatsächlichen Einschränkungen des Computers zu akzeptieren und die numerisch korrekte Version zu verwenden, die standardmäßig generiert wird. Aber wenn der Codegenerator dies nicht tut, besorgen Sie sich einen, der dies tut (oder hacken Sie den vorhandenen Code).

Fazit: Das Kompilieren und Verknüpfen von 2,8 GB Code funktioniert nicht und sollte nicht zum Arbeiten gezwungen werden. Finde einen anderen Weg.


3

Einige Vorschläge: - Für Größe optimieren (-Os). Machen Sie Ihre Inline-Funktionsaufrufe, normale Funktionsaufrufe. Aktivieren Sie das String-Pooling.

Versuchen Sie, die Dinge in verschiedene DLLs aufzuteilen (gemeinsam genutzte Objekte, .so für Linux, .dylib für Mac OS X). Stellen Sie sicher, dass sie entladen werden können. Implementieren Sie dann etwas, um Dinge bei Bedarf zu laden und sie freizugeben, wenn sie nicht benötigt werden.

Wenn nicht, teilen Sie Ihren Code in verschiedene ausführbare Dateien auf und verwenden Sie etwas, um zwischen ihnen zu kommunizieren (Pipes, Sockets, sogar Schreiben / Lesen in Dateien). Unbeholfen, aber welche Möglichkeiten haben Sie?

Völlig alternativ: - Verwenden Sie eine dynamische Sprache mit JIT . Ganz oben auf meinem Kopf - verwenden Sie LuaJIT - und schreiben Sie viele dieser Ausdrücke in Lua oder anderen solchen Sprachen und Laufzeiten neu (regenerieren Sie sie?) , Um Code zu sammeln.

LuaJIT ist sehr effizient und schlägt manchmal C / C ++ für bestimmte Dinge, aber oft sehr nahe (manchmal kann es aufgrund der schlechten Speicherbereinigung langsam sein). Überzeugen Sie sich selbst:

http://luajit.org/performance_x86.html

Laden Sie die scimark2.luaDatei von dort herunter und vergleichen Sie sie mit der "C" -Version (google it) - oft sind die Ergebnisse sehr ähnlich.


2

Der Linker versucht, 32-Bit-Verschiebungsversätze innerhalb einer Binärdatei zu generieren, die diese Einschränkungen irgendwie überschritten hat. Versuchen Sie, den Adressraumbedarf des Hauptprogramms zu reduzieren.

Können Sie einen Teil / den größten Teil des Objektcodes in eine oder mehrere Bibliotheken aufteilen (auch kompiliert mit -fpic / -fPIC)? Generieren Sie dann eine nicht statische Binärdatei, die mit diesen Bibliotheken verknüpft ist. Die Bibliotheken befinden sich in diskreten Speicherblöcken, und Ihre Verschiebungsversätze sind eher dynamisch / absolut (64-Bit) als relativ (32-Bit).


2

Diese Ausdrücke sehen für mich sehr nach einer alternierenden Serie aus. Ich weiß nicht, wie der Rest des Codes aussieht, aber es scheint nicht so schwer zu sein, den generierenden Ausdruck abzuleiten. Es würde sich wahrscheinlich auch zur Ausführungszeit lohnen, insbesondere wenn Sie 2,8 GB 2 KB ungerollten Code haben.


1

Dies scheint das Ergebnis einer fehlerhaften Codegenerierung zu sein, möglicherweise durch symbolische Algebra und / oder manuelles Abrollen. Es ist bekannt, dass symbolische Manipulationen in der Tiefe des Ausdrucksbaums oder des Rechengraphen exponentiell wachsen. Es ist wahrscheinlich, dass hier eine automatische Differenzierung verwendet werden kann, was die Codegröße ziemlich klein machen und auch die Ausführung dramatisch beschleunigen würde.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.