Dlaczego początkowa alokacja w C ++ jest o wiele większa niż w C?


138

Korzystając z tego samego kodu, po prostu zmiana kompilatora (z kompilatora C na kompilator C ++) zmieni ilość przydzielonej pamięci. Nie jestem do końca pewien, dlaczego tak jest i chciałbym to lepiej zrozumieć. Jak dotąd najlepszą odpowiedzią, jaką otrzymałem, jest „prawdopodobnie strumienie I / O”, co nie jest zbyt opisowe i sprawia, że ​​zastanawiam się nad aspektem C ++ „nie płacisz za to, czego nie używasz”.

Używam kompilatorów Clang i GCC, odpowiednio wersje 7.0.1-8 i 8.3.0-6. Mój system działa na Debianie 10 (Buster), najnowszym. Testy porównawcze są wykonywane za pośrednictwem Valgrind Massif.

#include <stdio.h>

int main() {
    printf("Hello, world!\n");
    return 0;
}

Użyty kod się nie zmienia, ale niezależnie od tego, czy kompiluję jako C czy C ++, zmienia wyniki testu porównawczego Valgrind. Wartości pozostają jednak spójne we wszystkich kompilatorach. Przydziały czasu działania (szczyt) dla programu wyglądają następująco:

  • GCC (C): 1032 bajtów (1 KB)
  • G ++ (C ++): 73744 bajty, (~ 74 KB)
  • Clang (C): 1032 bajtów (1 KB)
  • Clang ++ (C ++): 73744 bajty (~ 74 KB)

Do kompilacji używam następujących poleceń:

clang -O3 -o c-clang ./main.c
gcc -O3 -o c-gcc ./main.c
clang++ -O3 -o cpp-clang ./main.cpp
g++ -O3 -o cpp-gcc ./main.cpp

W przypadku Valgrind uruchamiam valgrind --tool=massif --massif-out-file=m_compiler_lang ./compiler-langkażdy kompilator i język, a następnie ms_printwyświetlam szczyty.

Czy ja tu robię coś złego?


11
Na początek, jak budujesz? Z jakich opcji korzystasz? A jak mierzysz? Jak prowadzisz Valgrind?
Jakiś programista koleś

17
Jeśli dobrze pamiętam, współczesne kompilatory C ++ muszą stosować model wyjątków, w którym nie ma wydajności do wejścia do trybloku kosztem większej ilości pamięci, być może z tabelą skoków lub czymś podobnym. Może spróbuj kompilować bez wyjątków i zobacz, jaki to ma wpływ. Edycja: W rzeczywistości spróbuj iteracyjnie wyłączyć różne funkcje C ++, aby zobaczyć, jaki wpływ ma to na zużycie pamięci.
François Andrieux

3
Podczas kompilacji z clang++ -xczamiast clang, był tam ten sam przydział, co zdecydowanie sugeruje, że jest to spowodowane powiązanymi bibliotekami
Justin

14
@bigwillydos To jest rzeczywiście C ++, nie widzę żadnej części specyfikacji C ++, którą łamie ... Poza tym, że potencjalnie zawiera stdio.h zamiast cstdio, ale jest to dozwolone przynajmniej w starszej wersji C ++. Jak myślisz, co jest „zniekształcone” w tym programie?
Vality

4
Wydaje mi się podejrzane, że te kompilatory gcc i clang generują dokładnie taką samą liczbę bajtów w Ctrybie i dokładnie taką samą liczbę bajtów w C++trybie. Czy popełniłeś błąd w transkrypcji?
RonJohn

Odpowiedzi:


149

Użycie sterty pochodzi ze standardowej biblioteki C ++. Przydziela pamięć do użytku wewnętrznej biblioteki podczas uruchamiania. Jeśli nie utworzysz linku przeciwko temu, różnica między wersjami C i C ++ powinna wynosić zero. Dzięki GCC i Clang możesz skompilować plik za pomocą:

g ++ -Wl, - w razie potrzeby main.cpp

Poinstruuje to konsolidator, aby nie łączył się z nieużywanymi bibliotekami. W przykładowym kodzie biblioteka C ++ nie jest używana, więc nie powinna być połączona z biblioteką standardową C ++.

Możesz to również przetestować z plikiem C. Jeśli kompilujesz z:

gcc main.c -lstdc ++

Użycie sterty pojawi się ponownie, nawet jeśli utworzyłeś program w C.

Użycie sterty zależy oczywiście od konkretnej implementacji biblioteki C ++, której używasz. W twoim przypadku jest to biblioteka GNU C ++, libstdc ++ . Inne implementacje mogą nie przydzielać takiej samej ilości pamięci lub mogą nie przydzielać żadnej pamięci w ogóle (przynajmniej nie przy uruchamianiu). Na przykład biblioteka LLVM C ++ ( libc ++ ) nie przydziela sterty przy uruchamianiu, przynajmniej w moim Linuksie maszyna:

clang ++ -stdlib = libc ++ main.cpp

Użycie sterty jest tym samym, co brak łączenia z nią w ogóle.

(Jeśli kompilacja się nie powiedzie, prawdopodobnie libc ++ nie jest zainstalowane. Nazwa pakietu zwykle zawiera „libc ++” lub „libcxx”).


50
Widząc tę ​​odpowiedź, pierwszą myślą jest: „ Jeśli ta flaga pomaga zredukować niepotrzebne obciążenie, dlaczego nie jest włączona domyślnie? ”. Czy jest na to dobra odpowiedź?
Nat,

4
@Nat Domyślam się, że w czasie programowania kompilacja jest wolniejsza. Gdy jesteś gotowy do utworzenia wersji wydania, możesz ją włączyć. Również w normalnej / dużej bazie kodu różnica może być minimalna (jeśli używasz wielu bibliotek STD itp.)
DarcyThomas

24
@Nat -Wl,--as-neededFlaga usuwa biblioteki określone we -lflagach, których w rzeczywistości nie używasz. Więc jeśli nie korzystasz z biblioteki, po prostu nie umieszczaj na niej linków. Nie potrzebujesz do tego tej flagi. Jeśli jednak twój system kompilacji dodaje zbyt wiele bibliotek i wyczyszczenie ich wszystkich i powiązanie tylko tych potrzebnych byłoby dużo pracy, możesz zamiast tego użyć tej flagi. Biblioteka standardowa jest jednak wyjątkiem, ponieważ jest automatycznie połączona z. Więc to jest przypadek narożny.
Nikos C.

36
@Nat - w razie potrzeby może mieć niepożądane efekty uboczne, działa, sprawdzając, czy używasz dowolnego symbolu biblioteki i wyrzuca te, które nie przejdą testu. ALE: biblioteka może również wykonywać różne rzeczy niejawnie, na przykład, jeśli masz w bibliotece statyczną instancję C ++, jej konstruktor zostanie wywołany automatycznie. Są rzadkie przypadki, w których biblioteka, do której nie dzwonisz jawnie, jest konieczna, ale istnieją.
Norbert Lange

3
@NikosC. Systemy kompilacji nie wiedzą automatycznie, jakich symboli używa twoja aplikacja i które biblioteki je implementują (różni się w zależności od kompilatorów, archiwów, dystrybucji i bibliotek c / c ++). Uzyskanie tego jest raczej kłopotliwe, przynajmniej dla podstawowych bibliotek wykonawczych. Ale w rzadkich przypadkach, gdy potrzebujesz biblioteki, powinieneś po prostu użyć - nie-w razie potrzeby do tego i pozostawić - tak jak jest potrzebne wszędzie indziej. Przykładem zastosowania, który widziałem, są biblioteki do śledzenia / debugowania (lttng) i biblioteki, które wykonują coś w rodzaju uwierzytelniania / łączenia.
Norbert Lange

16

Ani GCC, ani Clang nie są kompilatorami - w rzeczywistości są programami sterowników narzędzi. Oznacza to, że wywołują kompilator, asembler i konsolidator.

Jeśli skompilujesz swój kod za pomocą kompilatora C lub C ++, otrzymasz ten sam wyprodukowany zestaw. Asembler wyprodukuje te same obiekty. Różnica polega na tym, że sterownik toolchain będzie dostarczał różne dane wejściowe do konsolidatora dla dwóch różnych języków: różnych startów (C ++ wymaga kodu do wykonywania konstruktorów i destruktorów dla obiektów ze statycznym lub lokalnym wątkiem czasu trwania na poziomie przestrzeni nazw i wymaga infrastruktury dla stosu ramki do obsługi rozwijania podczas przetwarzania wyjątków), standardową bibliotekę C ++ (która również zawiera obiekty o statycznym czasie trwania na poziomie przestrzeni nazw) i prawdopodobnie dodatkowe biblioteki uruchomieniowe (na przykład libgcc z infrastrukturą rozwijania stosu).

Krótko mówiąc, to nie kompilator powoduje zwiększenie rozmiaru, ale linkowanie rzeczy, które wybrałeś, wybierając język C ++.

To prawda, że ​​C ++ ma filozofię „płać tylko za to, czego używasz”, ale używając języka, płacisz za to. Możesz wyłączyć części języka (RTTI, obsługa wyjątków), ale wtedy nie używasz już C ++. Jak wspomniano w innej odpowiedzi, jeśli w ogóle nie korzystasz z biblioteki standardowej, możesz poinstruować sterownik, aby to pominął (--Wl, - w razie potrzeby), ale jeśli nie zamierzasz używać żadnej z funkcji C ++ lub jego biblioteki, dlaczego w ogóle wybierasz C ++ jako język programowania?


Fakt, że włączenie obsługi wyjątków kosztuje, nawet jeśli w rzeczywistości go nie używasz, jest problemem. To nie jest normalne w przypadku funkcji C ++ w ogóle i jest to coś, co grupy robocze ds. Standardów C ++ próbują wymyślić sposoby naprawienia. Zobacz przemówienie Herba Suttera na ACCU 2019 De-fragmenting C ++: Making wyjątki bardziej przystępne i użyteczne . Jest to jednak niefortunny fakt w obecnym C ++. Tradycyjne wyjątki C ++ będą prawdopodobnie zawsze miały ten koszt, nawet jeśli / kiedy nowe mechanizmy nowych wyjątków zostaną dodane za pomocą słowa kluczowego.
Peter Cordes
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.