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-lang
każdy kompilator i język, a następnie ms_print
wyświetlam szczyty.
Czy ja tu robię coś złego?
źródło
try
bloku 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.clang++ -xc
zamiastclang
, był tam ten sam przydział, co zdecydowanie sugeruje, że jest to spowodowane powiązanymi bibliotekamiC
trybie i dokładnie taką samą liczbę bajtów wC++
trybie. Czy popełniłeś błąd w transkrypcji?Odpowiedzi:
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ą:
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:
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:
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”).
źródło
-Wl,--as-needed
Flaga usuwa biblioteki określone we-l
flagach, 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.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?
źródło