Muszę poważnie zoptymalizować rozmiar mojego pliku wykonywalnego ( ARM
programowanie) i zauważyłem, że w moim obecnym schemacie kompilacji ( gcc
+ ld
) nieużywane symbole nie są usuwane.
Użycie arm-strip --strip-unneeded
dla wynikowych plików wykonywalnych / bibliotek nie zmienia rozmiaru wyjściowego pliku wykonywalnego (nie mam pojęcia dlaczego, może po prostu nie może) .
Jaki byłby sposób (jeśli istnieje) zmodyfikowania potoku budowania, tak aby nieużywane symbole zostały usunięte z pliku wynikowego?
Ja nawet nie myśleć o tym, ale mój obecny osadzony środowisko nie jest bardzo „mocny” i oszczędność nawet 500K
z 2M
wynikami w bardzo ładnym wzrost wydajności załadunku.
Aktualizacja:
Niestety obecna gcc
wersja, której używam, nie ma takiej -dead-strip
opcji, a -ffunction-sections... + --gc-sections
for ld
nie daje żadnej znaczącej różnicy w wynikowym wyniku.
Jestem zszokowany, że stało się to nawet problemem, ponieważ byłem pewien, że gcc + ld
powinno to automatycznie usunąć nieużywane symbole (dlaczego w ogóle muszą je zachować?).
boost
bibliotek,.exe
plik wynikowy zawiera wiele nieużywanych plików obiektowych i ze względu na specyfikacje mojego obecnego osadzonego środowiska wykonawczego uruchomienie10mb
aplikacji trwa znacznie dłużej niż na przykład uruchomienie500k
aplikacji.Odpowiedzi:
W przypadku GCC odbywa się to w dwóch etapach:
Najpierw skompiluj dane, ale powiedz kompilatorowi, aby rozdzielił kod na oddzielne sekcje w jednostce tłumaczenia. Zostanie to zrobione dla funkcji, klas i zmiennych zewnętrznych przy użyciu następujących dwóch flag kompilatora:
Połącz jednostki tłumaczeniowe razem przy użyciu flagi optymalizacji linkera (powoduje to, że linker odrzuca sekcje bez odniesień):
Więc jeśli masz jeden plik o nazwie test.cpp, w którym zadeklarowano dwie funkcje, ale jedna z nich była nieużywana, możesz pominąć nieużywany za pomocą następującego polecenia do gcc (g ++):
(Zauważ, że -Os to dodatkowa flaga kompilatora, która mówi GCC, aby zoptymalizować pod kątem rozmiaru)
źródło
mingw
tym nie działa podczas statycznego łączenia bibliotek libstdc ++ i libgcc z flagą-static
. Opcja konsolidatora-strip-all
pomaga całkiem sporo, ale nadal wygenerowany plik wykonywalny (lub dll) jest około 4 razy większy niż to, co wygenerowałoby Visual Studio. Chodzi o to, że nie mam kontroli nad tym, jaklibstdc++
został skompilowany. Powinna istniećld
jedyna opcja.Jeśli ten wątek wierzyć, trzeba dostarczyć
-ffunction-sections
i-fdata-sections
do gcc, co wywrze każdej funkcji i obiekt danych w osobnej sekcji. Następnie dajesz i--gc-sections
GNU ld usuwa nieużywane sekcje.źródło
Będziesz chciał sprawdzić swoje dokumenty pod kątem wersji gcc & ld:
Jednak dla mnie (OS X gcc 4.0.1) znajduję je dla ld
I ta pomocna opcja
W man gcc / g ++ jest również uwaga, że pewne rodzaje eliminacji martwego kodu są wykonywane tylko wtedy, gdy optymalizacja jest włączona podczas kompilacji.
Chociaż te opcje / warunki mogą nie odpowiadać Twojemu kompilatorowi, sugeruję, abyś poszukał czegoś podobnego w swoich dokumentach.
źródło
mingw
.-dead_strip
nie wchodzi wgcc
grę.Mogą też pomóc nawyki programistyczne; np. dodaj
static
do funkcji, do których nie można uzyskać dostępu poza określonym plikiem; używaj krótszych nazw symboli (może trochę pomóc, prawdopodobnie nie za bardzo); używać wconst char x[]
miarę możliwości; ... ten artykuł , chociaż mówi o dynamicznych obiektach współdzielonych, może zawierać sugestie, których przestrzeganie może pomóc zmniejszyć ostateczny rozmiar wyjścia binarnego (jeśli celem jest ELF).źródło
.so
w systemie Linux), więc nazwy symboli muszą zostać zachowane, aby interfejsy API, takie jakctypes
moduł FFI Pythona, mogły ich używać do wyszukiwania symboli według nazwy w czasie wykonywania.Odpowiedź jest
-flto
. Musisz przekazać to zarówno do kompilacji, jak i do kroków łączenia, w przeciwnym razie nic nie zrobi.W rzeczywistości działa bardzo dobrze - zmniejszyłem rozmiar programu mikrokontrolera, który napisałem, do mniej niż 50% jego poprzedniego rozmiaru!
Niestety wydawało się to trochę wadliwe - miałem przypadki, gdy rzeczy nie były poprawnie budowane. Mogło to być spowodowane systemem kompilacji, którego używam (QBS; jest bardzo nowy), ale w każdym razie zalecałbym włączenie go tylko dla ostatecznej kompilacji, jeśli to możliwe, i dokładne przetestowanie tej kompilacji.
źródło
-flto
, ponieważ nie rozumiem, co robi za kulisami.-flto
że nie kompiluje każdego pliku do asemblacji, kompiluje je do LLVM IR, a następnie końcowe łącze kompiluje je tak, jakby były w jednej jednostce kompilacji. Oznacza to, że może wyeliminować nieużywane funkcje i wbudowane nie-static
jedynki, a prawdopodobnie także inne rzeczy. Zobacz llvm.org/docs/LinkTimeOptimization.htmlChociaż nie chodzi tylko o symbole, jeśli chodzi o rozmiar - zawsze kompiluj z flagami
-Os
i-s
.-Os
optymalizuje wynikowy kod pod kątem minimalnego rozmiaru pliku wykonywalnego i-s
usuwa tablicę symboli i informacje o relokacji z pliku wykonywalnego.Czasami - jeśli pożądany jest mały rozmiar - zabawa z różnymi flagami optymalizacji może - lub nie - mieć znaczenie. Na przykład przełączanie
-ffast-math
i / lub-fomit-frame-pointer
czasami może zaoszczędzić nawet dziesiątki bajtów.źródło
-ffast-math
siałem spustoszenie w całkowicie zgodnym ze standardami kodzie C ++, więc nigdy nie polecałbym tego.Wydaje mi się, że odpowiedź udzielona przez Nemo jest prawidłowa. Jeśli te instrukcje nie działają, problem może być związany z wersją gcc / ld, której używasz, jako ćwiczenie skompilowałem przykładowy program, korzystając z instrukcji opisanych tutaj
Następnie skompilowałem kod przy użyciu coraz bardziej agresywnych przełączników usuwania martwego kodu:
Te parametry kompilacji i łączenia generowały pliki wykonywalne o rozmiarze, odpowiednio, 8457, 8164 i 6160 bajtów, co stanowiło największy wkład pochodzący z deklaracji „strip-all”. Jeśli nie możesz stworzyć podobnych redukcji na swojej platformie, być może Twoja wersja gcc nie obsługuje tej funkcjonalności. Używam gcc (4.5.2-8ubuntu4), ld (2.21.0.20110327) na Linux Mint 2.6.38-8-generic x86_64
źródło
strip --strip-unneeded
działa tylko na tablicy symboli twojego pliku wykonywalnego. W rzeczywistości nie usuwa żadnego kodu wykonywalnego.Biblioteki standardowe osiągają oczekiwany rezultat, dzieląc wszystkie swoje funkcje na osobne pliki obiektowe, które są łączone za pomocą
ar
. Jeśli następnie połączysz powstałe archiwum jako bibliotekę (tj. Dasz opcję-l your_library
ld), wtedy ld będzie zawierał tylko pliki obiektowe, a tym samym symbole, które są faktycznie używane.Możesz również znaleźć niektóre odpowiedzi na to podobne pytanie dotyczące użytkowania.
źródło
Nie wiem, czy to pomoże w twojej obecnej sytuacji, ponieważ jest to nowa funkcja, ale możesz określić widoczność symboli w sposób globalny. Przekazywanie
-fvisibility=hidden -fvisibility-inlines-hidden
podczas kompilacji może pomóc konsolidatorowi w późniejszym pozbyciu się niepotrzebnych symboli. Jeśli tworzysz plik wykonywalny (w przeciwieństwie do biblioteki współdzielonej), nie ma nic więcej do zrobienia.Więcej informacji (i szczegółowe podejście dla np. Bibliotek) jest dostępnych na wiki GCC .
źródło
Z podręcznika GCC 4.2.1, sekcja
-fwhole-program
:źródło
-flto
.Możesz użyć strip binarnego na pliku obiektowym (np. Wykonywalnym), aby usunąć z niego wszystkie symbole.
Uwaga: zmienia sam plik i nie tworzy kopii.
źródło