Błąd kompilacji GCC z> 2 GB kodu

108

Mam ogromną liczbę funkcji w sumie około 2,8 GB kodu wynikowego (niestety nie ma sposobu obejścia, obliczenia naukowe ...)

Kiedy próbuję je połączyć, otrzymuję (oczekiwane) relocation truncated to fit: R_X86_64_32Sbłędy, które miałem nadzieję ominąć, określając flagę kompilatora -mcmodel=medium. Wszystkie biblioteki połączone dodatkowo, nad którymi mam kontrolę, są kompilowane z -fpicflagą.

Mimo to błąd nadal występuje i zakładam, że niektóre biblioteki, do których odsyłam, nie są skompilowane za pomocą PIC.

Oto błąd:

/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

Biblioteki systemowe, do których odsyłam:

-lgfortran -lm -lrt -lpthread

Jakieś wskazówki, gdzie szukać problemu?

EDYCJA: Przede wszystkim dziękuję za dyskusję ... Aby trochę wyjaśnić, mam setki funkcji (każda o rozmiarze około 1 MB w oddzielnych plikach obiektowych), takich jak:

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

Obiekt sjest stosunkowo mały i zachowuje potrzebne stałe x14, x15, ..., ds0, ... itd., A tipo prostu zwraca double z zewnętrznej biblioteki. Jak widać, csc[]jest to wstępnie obliczona mapa wartości, która jest również oceniana w oddzielnych plikach obiektowych (ponownie setkach po około 1 MB każdy) o następującej postaci:

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

    // ...
}

O to chodzi. Ostatnim krokiem jest wtedy po prostu wywołanie wszystkich func[i]i podsumowanie wyniku.

Jeśli chodzi o fakt, że jest to raczej szczególny i nietypowy przypadek: Tak, jest. To jest to, z czym ludzie muszą sobie radzić, próbując wykonać bardzo precyzyjne obliczenia dla fizyki cząstek elementarnych.

EDIT2: Powinienem też dodać, że x12, x13 itd. Nie są tak naprawdę stałymi. Są one ustawiane na określone wartości, wszystkie te funkcje są uruchamiane i zwracany jest wynik, a następnie wybierany jest nowy zestaw x12, x13 itd., Aby wygenerować następną wartość. I trzeba to zrobić 10 ^ 5 do 10 ^ 6 razy ...

EDIT3: Dziękuję za sugestie i dotychczasową dyskusję ... Spróbuję jakoś zwinąć pętle po wygenerowaniu kodu, nie jestem pewien, jak to dokładnie, szczerze mówiąc, ale to najlepszy wybór.

Swoją drogą, nie próbowałem się ukrywać za „to jest obliczenia naukowe - nie ma sposobu na optymalizację”. Tyle, że podstawą tego kodu jest coś, co wychodzi z „czarnej skrzynki”, do której nie mam realnego dostępu, a ponadto całość działała świetnie na prostych przykładach i głównie czuję się przytłoczony tym, co dzieje się w prawdziwym aplikacja światowa ...

EDIT4: Więc udało mi się zmniejszyć rozmiar kodu cscdefinicji o około jedną czwartą, upraszczając wyrażenia w systemie algebry komputerowej ( Mathematica ). Widzę teraz również sposób na zmniejszenie go o inny rząd wielkości, stosując kilka innych sztuczek przed wygenerowaniem kodu (co zmniejszyłoby tę część do około 100 MB) i mam nadzieję, że ten pomysł zadziała.

Teraz związane z twoimi odpowiedziami: próbuję ponownie zwinąć pętle w funcs, gdzie CAS niewiele pomoże, ale mam już kilka pomysłów. Na przykład, sortując wyrażenia według zmiennych, takich jak x12, x13,..., przeanalizuj je cscw Pythonie i wygeneruj tabele, które będą ze sobą powiązane. Następnie mogę przynajmniej wygenerować te części jako pętle. Ponieważ wydaje się, że jest to do tej pory najlepsze rozwiązanie, oznaczam to jako najlepszą odpowiedź.

Chciałbym jednak również wyrazić uznanie dla VJo. GCC 4.6 rzeczywiście działa znacznie lepiej, tworzy mniejszy kod i jest szybsze. Korzystanie z dużego modelu działa w kodzie takim, jakim jest. Więc technicznie jest to poprawna odpowiedź, ale zmiana całej koncepcji jest znacznie lepszym podejściem.

Dziękuję wszystkim za sugestie i pomoc. Jeśli ktoś jest zainteresowany, opublikuję ostateczny wynik, gdy tylko będę gotowy.

UWAGI: Kilka uwag do innych odpowiedzi: Kod, który próbuję uruchomić, nie pochodzi z rozszerzenia prostych funkcji / algorytmów i głupiego, niepotrzebnego rozwijania. W rzeczywistości dzieje się tak, że rzeczy, od których zaczynamy, to dość skomplikowane obiekty matematyczne i doprowadzenie ich do postaci obliczalnej numerycznie generuje te wyrażenia. Problem tkwi w rzeczywistości w podstawowej teorii fizycznej. Złożoność wyrażeń pośrednich skaluje się silnie, co jest dobrze znane, ale gdy łączymy wszystkie te rzeczy w coś fizycznie mierzalnego - obserwowalnego - sprowadza się to tylko do kilku bardzo małych funkcji, które stanowią podstawę wyrażeń. (Zdecydowanie jest coś „nie tak” w tym względzie z ogólnym i dostępnym tylkoansatz, który jest nazywany „teorią perturbacji”). Próbujemy przenieść ten ansatz na inny poziom, który nie jest już wykonalny analitycznie i gdzie nie są znane podstawy potrzebnych funkcji. Więc próbujemy to brutalnie wymusić w ten sposób. Nie jest to najlepszy sposób, ale miejmy nadzieję, że taki, który w końcu pomoże nam w zrozumieniu fizyki ...

OSTATNIA EDYCJA: Dzięki wszystkim waszym sugestiom udało mi się znacznie zmniejszyć rozmiar kodu, używając Mathematica i modyfikacji generatora kodu dla funcs, nieco zgodnej z górną odpowiedzią :)

Uprościłem cscfunkcje w Mathematica, zmniejszając je do 92 MB. To jest nieredukowalna część. Pierwsze próby trwały wieki, ale po kilku optymalizacjach trwa to teraz w ciągu około 10 minut na jednym procesorze.

Wpływ na funcs był dramatyczny: cały rozmiar kodu dla nich spadł do około 9 MB, więc teraz kod zawiera się w zakresie 100 MB. Teraz warto włączyć optymalizacje, a wykonanie jest dość szybkie.

Jeszcze raz dziękuję wszystkim za sugestie, wiele się nauczyłem.

bbtrb
źródło
17
Jeśli masz tak dużo danych, powinieneś przenieść je z plików źródłowych, a zamiast mmaptego samodzielnie z zewnętrznego pliku binarnego w czasie wykonywania.
R .. GitHub PRZESTAŃ POMÓC W LODZIE
3
Czy mógłbyś podać przykład jednej (lub dwóch) z tych funkcji? To naprawdę wygląda dziwnie. Możesz także ładować te funkcje dynamicznie za pomocą funkcji dl *.
Patrick Schlüter
7
@bbtrb: Mój pierwszy instynkt jest podobny do R .., brzmi jak problem projektowy. Wprawdzie nie wiem, co jest powszechne w kręgach informatyki naukowej, ale nigdy nie słyszałem o kimś, kto próbowałby połączyć plik obiektowy o rozmiarze 2,8 GB lub cokolwiek, co jest mu bliskie, i nie jestem pewien, czy GCC naprawdę by to obsługiwał. Szczerze mówiąc, spodziewałbym się, że każda kropla kodu tego rozmiaru będzie czystym spaghetti.
Nicholas Knight
46
absolutnie nie ma możliwości, aby optymalne rozwiązanie problemu obejmowało 2 GB pliku obiektowego.
David Heffernan
35
nie umieszczaj swoich danych w kodzie
David Heffernan,

Odpowiedzi:

53

Więc masz już program, który generuje ten tekst:

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

i

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

dobrze?

Jeśli wszystkie twoje funkcje mają podobny „format” (pomnóż n liczb m razy i dodaj wyniki - lub coś podobnego), myślę, że możesz to zrobić:

  • zmień program generatora tak, aby wyprowadzał przesunięcia zamiast łańcuchów (tj. zamiast łańcucha "s.ds0" wygeneruje offsetof(ProcessVars, ds0)
  • utworzyć tablicę takich przesunięć
  • napisz ewaluator, który zaakceptuje powyższą tablicę i adresy bazowe wskaźników struktury i wygeneruje wynik

Tablica + ewaluator będzie reprezentować tę samą logikę, co jedna z twoich funkcji, ale tylko oceniający będzie kodem. Tablica jest „danymi” i może być generowana w czasie wykonywania lub zapisywana na dysku i odczytywana fragmentami lub w pliku mapowanym w pamięci.

Dla konkretnego przykładu w func1 wyobrazić jak można przepisać funkcji poprzez oceniającego gdybyś miał dostęp do adresu bazowego si cscoraz wektorem jak reprezentacji stałych i przesunięć trzeba dodać do adresów bazowych, aby dostać się x14, ds8icsc[51370]

Musisz utworzyć nową formę „danych”, która będzie opisywać, jak przetwarzać rzeczywiste dane przekazywane do ogromnej liczby funkcji.

Andrei
źródło
45

Interfejs ABI x86-64 używany przez Linuksa definiuje „duży model” specjalnie w celu uniknięcia takich ograniczeń rozmiaru, który obejmuje 64-bitowe typy relokacji dla GOT i PLT. (Zobacz tabelę w sekcji 4.4.2 i sekwencje instrukcji w 3.5.5, które pokazują, jak są używane.)

Ponieważ twoje funkcje zajmują 2,8 GB, nie masz szczęścia, ponieważ gcc nie obsługuje dużych modeli. To, co możesz zrobić, to zreorganizować swój kod w taki sposób, aby umożliwić podzielenie go na biblioteki współdzielone, które można by dynamicznie łączyć.

Jeśli nie jest to możliwe, jak ktoś sugerował, zamiast umieszczać dane w kodzie (kompilować i łączyć), ponieważ jest ogromny, możesz go załadować w czasie wykonywania (jako normalny plik lub możesz go mmap).

EDYTOWAĆ

Wygląda na to, że duży model jest obsługiwany przez gcc 4.6 (zobacz tę stronę ). Możesz tego spróbować, ale powyższe nadal dotyczy reorganizacji kodu.

BЈовић
źródło
Więc mówisz, że kiedy pogrupowałbym pliki obiektowe w kilku małych bibliotekach współdzielonych, pokonałbym ograniczenia?
bbtrb
3
@bbtrb Right. Ale nadal szukałbym innego sposobu implementacji twoich funkcji. Założę się, że twoja kompilacja trwa wiecznie
BЈовић
18
WTF? Ten kod musi zostać wygenerowany przez jakiś skrypt; nikt nie pisze ręcznie megabajtów kodu! Ta sama logika, która generuje kod, może również zostać użyta do uruchomienia obliczeń.
zvrba
6
Gorąco polecam wypróbowanie gcc 4.6, jest bardzo prawdopodobne, że stworzy lepszy kod dla tego programu niż gcc 4.1; może być nawet w stanie zmieścić całość do 2 GB bez konieczności robienia czegokolwiek sprytnego, eliminując problem (spróbuj kombinacji -Os, -fcały-program i -flto - przy takiej ilości kodu optymalizacja pod względem rozmiaru jest optymalizacja pod kątem szybkości). Jednakże, jeśli to nie pomoże, powinieneś również mieć świadomość, że aby duży model działał, będziesz musiał przebudować przynajmniej część biblioteki C w dużym modelu (crt * .o, libc_nonshared.a i libpthread_nonshared.a).
zwol
1
@bdonlan Łączenie statyczne jest również możliwe.
zvrba
37

W przypadku programu po tej stronie błędy pamięci podręcznej kodu z dużym prawdopodobieństwem przekroczą koszty zapętlenia w czasie wykonywania. Zalecałbym, abyś wrócił do swojego generatora kodu i wygenerował zwartą reprezentację tego, co chce ocenić (tj. Taką, która prawdopodobnie zmieści się w pamięci podręcznej D), a następnie uruchom ją za pomocą interpretera w programie. Możesz również sprawdzić, czy możesz wyodrębnić mniejsze jądra, które nadal mają znaczną liczbę operacji, a następnie użyć ich jako „instrukcji” w interpretowanym kodzie.

bdonlan
źródło
21

Błąd występuje, ponieważ masz za dużo KODU, a nie danych! Wskazuje na to na przykład __libc_csu_fini(która jest funkcją), do której się odwołujemy, _starta relokacja jest obcinana w celu dopasowania. Oznacza to, że _start(prawdziwy punkt wejścia programu) próbuje wywołać tę funkcję poprzez SIGNED 32-bitowe przesunięcie, które ma tylko zakres 2 GB. Ponieważ całkowita ilość kodu wynikowego wynosi ~ 2,8 GB, fakty się sprawdzają.

Gdybyś mógł przeprojektować struktury danych, znaczną część kodu można by „skompresować”, przepisując ogromne wyrażenia na proste pętle.

Możesz także csc[]wykonać obliczenia w innym programie, zapisać wyniki w pliku i po prostu załadować je w razie potrzeby.

zvrba
źródło
Czy możesz podać przykład, jak przepisałbyś funkcje za pomocą prostych pętli? Nie podążam za tobą dokładnie. csc[]musi być obliczany bardzo często i chciałbym uniknąć operacji wejścia / wyjścia dysku.
bbtrb
4
@bbtr: Na przykład dla func1wyżej, coś jak: for (int i = 0; i < N; ++i) expr += constants[i].*s.x14*s.x15*csc[49300 + i];.
HighCommander4
@ HighCommander4: zdecydowanie się zgadzam. Jest tuż nad moją głową, jak automatycznie wygenerować coś takiego. Może z oddzielną tablicą przechowującą indeksy ...
bbtrb,
2
@bbtrb: Ponieważ nie ma sposobu, niecodzienna , że ktoś napisał dość źródło do produkcji 2.8GB kodu obiektowego ręcznie, zwłaszcza z takimi nazwami symboli un-pamięciowy, generator kodu musi zostały wykorzystane. Pracuj z tym.
Donal Fellows
15

Myślę, że wszyscy się zgadzają, że powinien być inny sposób robienia tego, co chcesz. Kompilowanie setek megabajtów (gigabajtów?) Kodu, łączenie go w plik wykonywalny o rozmiarze wielu gigabajtów i uruchamianie go po prostu brzmi bardzo nieefektywnie.

Jeśli dobrze rozumiem twój problem, używasz jakiegoś generatora kodu, G, do generowania zestawu funkcji, func1...Nktóre pobierają kilka map csc1...Mjako dane wejściowe. To, co chcesz zrobić, to obliczyć csc1...Mi uruchomić pętlę 1000000 razy dla różnych danych wejściowych i za każdym razem znaleźć s = func1 + func2 + ... + funcN. Nie określiłeś jednak, jak fucn1...Nsą one powiązane csc1...M.

Jeśli to wszystko prawda, wydaje się, że powinieneś być w stanie postawić problem na głowie w inny sposób, który może być znacznie łatwiejszy w zarządzaniu, a nawet prawdopodobnie szybszy (tj. Pozwalając pamięci podręcznej komputera na faktyczne działanie).

Poza praktycznym problemem związanym z rozmiarami plików obiektowych, Twój obecny program nie będzie wydajny, ponieważ nie lokalizuje dostępu do danych (zbyt wiele dużych map) i nie ma zlokalizowanego wykonywania kodu (zbyt wiele bardzo długich funkcji).

Co powiesz na rozbicie programu na 3 fazy: Faza 1 kompilacja csc1...Mi przechowywanie ich. Faza 2 buduj pojedynczo func, uruchamiaj ją 1000000 razy z każdym wejściem i zapisz wyniki. W fazie 3 znajdź sumę wyników zapisanych func1...Nwyników dla każdego wyczerpania 1 000 000 razy. Zaletą tego rozwiązania jest to, że można je łatwo ustawić równolegle na kilku niezależnych maszynach.

Edycja: @bbtrb, czy możesz gdzieś udostępnić jedną funkcję i jedną CSC? Wydają się być bardzo regularne i ściśliwe. Na przykład func1 wydaje się być po prostu sumą wyrażeń, z których każde składa się z 1 współczynnika, 2 indeksów do zmiennych w si 1 indeksu do csc. Więc można to zredukować do ładnej pętli. Jeśli udostępnisz pełne przykłady, na pewno można znaleźć sposoby na skompresowanie ich w pętle zamiast długich wyrażeń.

AlefSin
źródło
Tak, dobrze rozumiesz :) Jest jednak kilka problemów z twoją sugestią: 1. najgorsze funczależą od prawie wszystkich cscs i te liczby też trzeba obliczyć 10 ^ 6 razy. 2. Dane wejściowe zostaną uzyskane z adaptacyjnego integratora Monte Carlo, co oznacza, że ​​integrator musi znać pełny wynik w każdym punkcie, aby w razie potrzeby móc zredukować wynikowy błąd przez udoskonalenie siatki w pobliżu punktu. 3. Duże wyrażenia dla csc
trwałości
1
Czy to oznacza, że ​​nie można obliczyć każdego cscw każdej iteracji niezależnie od pozostałych? Gdyby były niezależne, nadal można by uruchomić każdy z nich 10 ^ 6 razy i zapisać wyniki. Jeśli jednak są między nimi zależności, być może musisz dowiedzieć się, który z nich jest powiązany z którym, coś w rodzaju wykresu zależności, a następnie spróbować sprawdzić, czy możesz podzielić go na wiele niezależnych pod-grafów. Podsumowując, myślę, że kluczem jest rozbicie problemu na wiele niezależnych podproblemów.
AlefSin
5

Jeśli poprawnie odczytam twoje błędy, tym, co sprawia, że ​​przenosisz limit, jest zainicjowana sekcja danych (gdyby to był kod, miałbyś znacznie więcej błędów IMHO). Czy masz duże tablice globalnych danych? W takim przypadku zrestrukturyzowałbym program tak, aby były przydzielane dynamicznie. Jeśli dane są zainicjowane, odczytałbym je z pliku konfiguracyjnego.

BTW widząc to:

(.text + 0x20): niezdefiniowane odniesienie do `main '

Myślę, że masz inny problem.

AProgrammer
źródło
1
Tak, masz rację, głupi błąd, ale to nie rozwiązuje pozostałych błędów.
bbtrb
3

Wydaje mi się, że kod dokonuje integracji numerycznej przy użyciu jakiejś metody adaptacyjnej głębokości. Niestety, generator kodu (a raczej autor generatora kodu) jest tak głupi, że generuje jedną funkcję na łatkę zamiast jednej na typ poprawki. W związku z tym wygenerował zbyt dużo kodu, aby można go było skompilować, a nawet gdyby można go było skompilować, jego wykonanie byłoby bolesne, ponieważ nic nigdy nie jest nigdzie udostępniane. (Czy możesz sobie wyobrazić ból wynikający z konieczności ładowania każdej strony kodu wynikowego z dysku, ponieważ nic nie jest nigdy udostępniane, więc system operacyjny zawsze może go eksmitować. Nie wspominając o pamięci podręcznej instrukcji, która będzie bezużyteczna).

Rozwiązaniem jest zaprzestanie rozwijania wszystkiego; w przypadku tego rodzaju kodu, chcesz zmaksymalizować udostępnianie, ponieważ narzut dodatkowych instrukcji dostępu do danych w bardziej złożonych wzorcach zostanie i tak pochłonięty przez koszt obsługi (przypuszczalnie) dużego bazowego zbioru danych. Możliwe jest również, że generator kodu zrobi to nawet domyślnie i że naukowiec zobaczył kilka opcji rozwijania (z uwagą, że czasami poprawiają one prędkość) i włączył je wszystkie naraz i teraz nalega, aby ten wynikający bałagan został zaakceptowany przez komputer, zamiast akceptować rzeczywiste ograniczenia maszyny i używać numerycznie poprawnej wersji, która jest generowana domyślnie. Ale jeśli generator kodu tego nie zrobi, zdobądź taki, który to zrobi (lub zhakuje istniejący kod).

Podsumowując: kompilacja i linkowanie 2,8 GB kodu nie działa i nie powinno być zmuszane do pracy. Znajdź inny sposób.

Donal Fellows
źródło
3

Kilka sugestii: - Zoptymalizuj rozmiar (-Os). Wykonuj wywołania funkcji inline, zwykłe wywołania funkcji. Włącz buforowanie ciągów.

Spróbuj podzielić te rzeczy na różne DLL (obiekty współdzielone, .so dla Linuksa, .dylib dla Mac OS X). Upewnij się, że można je rozładować. Następnie zaimplementuj coś, co załaduje rzeczy na żądanie i zwolnij je, gdy nie są potrzebne.

Jeśli nie, podziel kod na różne pliki wykonywalne i użyj czegoś do komunikacji między nimi (potoki, gniazda, a nawet zapis / odczyt do pliku). Niezdarny, ale jakie masz opcje?

Całkowicie alternatywne: - Użyj dynamicznego języka z JIT . Zaraz na mojej głowie - użyj LuaJIT - i przepisz (zregeneruj?) Wiele z tych wyrażeń w Lua lub innych tego typu językach i środowiskach wykonawczych, które umożliwiają zbieranie kodu bezużytecznego.

LuaJIT jest dość wydajny, czasami pokonuje C / C ++ dla pewnych rzeczy, ale często jest bardzo blisko (czasami może być powolny z powodu słabego zbierania śmieci). Sprawdź sam:

http://luajit.org/performance_x86.html

Pobierz scimark2.luaplik stamtąd i porównaj go z wersją "C" (wygoogluj to) - często wyniki są bardzo zbliżone.

malkia
źródło
2

Linker próbuje wygenerować 32-bitowe przesunięcia relokacji w pliku binarnym, które w jakiś sposób przekroczyły te ograniczenia. Spróbuj zmniejszyć wymagania dotyczące przestrzeni adresowej głównego programu.

Czy możesz podzielić część / większość kodu wynikowego na jedną lub więcej bibliotek (również skompilowanych z -fpic / -fPIC)? Następnie wygeneruj niestatyczny plik binarny, który łączy się z tymi bibliotekami. Biblioteki będą żyć w dyskretnych blokach pamięci, a przesunięcia relokacji będą dynamiczne / absolutne (64-bitowe), a nie względne (32-bitowe).

ajklbahu8geag
źródło
2

Te wyrażenia wyglądają dla mnie jak seria naprzemienna. Nie wiem, jak wygląda reszta kodu, ale nie wydaje się, aby wyprowadzenie wyrażenia generującego było takie trudne. Prawdopodobnie byłoby to warte również w czasie wykonywania, zwłaszcza jeśli masz 2,8 GB 2 KB niezrolowanego kodu.

Brian
źródło
1

Wygląda na to, że wynik generowania kodu poszedł źle, być może przez algebrę symboliczną i / lub ręczne rozwijanie. Powszechnie wiadomo, że manipulacje symboliczne rosną wykładniczo w głębi drzewa wyrażeń lub wykresu obliczeniowego. Jest prawdopodobne, że można tutaj zastosować automatyczne różnicowanie, co zmniejszyłoby rozmiar kodu, a także znacznie przyspieszyło wykonywanie.

Jed
źródło