Czy biblioteki zawierające tylko nagłówki są bardziej wydajne?

48

Założenia

  1. Jedną z zalet bibliotek tylko nagłówkowych dla C ++ jest to, że nie trzeba ich oddzielnie kompilować.

  2. W C i C ++ inlinema sens tylko wtedy, gdy funkcja jest zdefiniowana w pliku nagłówkowym *.

  3. Tradycyjnie w C używany jest układ .c / .h, w którym nagłówek reprezentuje minimalny publiczny interfejs jednostki tłumaczeniowej. Podobnie .cpp / hpp.

Pytanie

Czy biblioteki zawierające tylko nagłówki są generalnie bardziej wydajne pod względem kodu i czasu wykonania niż tradycyjny układ? Jeśli tak, to czy wynika to z dużej inlinizacji lub innych optymalizacji?

* - zdefiniowanie funkcji w nagłówku pozwala kompilatorowi zobaczyć implementację podczas kompilacji dowolnej jednostki tłumaczeniowej i praktycznie umożliwia wstawienie kodu

Vorac
źródło
2
Byłbyś zaskoczony, jak wiele współczesnych linkerów C ++ (GCC, MSVC, ICC itp.) Może wstawiać kod w oddzielne jednostki tłumaczeniowe. Powiedziałbym „ogólnie nie” z punktu widzenia wydajności, biorąc pod uwagę, ile razy optymalizatory linkerów przekroczyły moje oczekiwania i udało się inline inline (z wyłączeniem kontekstów dylib, w których pomocne może być wstawianie w nagłówku lub zapewnienie implementacji w oddzielnej statycznie połączonej bibliotece) . Jednak biblioteki zawierające tylko nagłówki, pod warunkiem, że są stabilne zarówno pod względem interfejsu, jak i implementacji, mogą być seksowne z uwagi na to, jak łatwo można je wdrażać w nowych projektach.
1
Nie wiem, czy to zasługuje na pełną odpowiedź, ale jedną wielką zaletą lib z nagłówkami jest łatwość instalacji i użytkowania: Pobieranie, #include „lib.h (pp)”, gotowe.
WeRelic

Odpowiedzi:

44

Jedną z zalet bibliotek tylko nagłówkowych dla C ++ jest to, że nie trzeba ich oddzielnie kompilować

Nie, to nie jest zaleta, wręcz przeciwnie - główna część biblioteki musi być kompilowana tak często, jak się ją włącza, nie tylko raz. Zwykle wydłuży to czas kompilacji. Jeśli jednak masz na myśli zalety wymienione tutaj w Wikipedii : ten artykuł mówi o zmniejszonym obciążeniu administracyjnym dotyczącym całego procesu kompilacji, pakowania i wdrażania.

W C i C ++ wstawianie ma sens tylko wtedy, gdy funkcja jest zdefiniowana w pliku nagłówkowym *

Zależy to od systemu kompilatora / linkera, ale wydaje mi się, że w przypadku większości istniejących kompilatorów C i C ++ jest to prawda.

Tradycyjnie w C używany jest układ .c / .h, w którym nagłówek reprezentuje minimalny publiczny interfejs jednostki tłumaczeniowej. Podobnie .cpp / hpp.

To jest w większości poprawne. Nagłówki klasy C ++ często zawierają więcej niż minimalny publiczny interfejs - zazwyczaj zawierają również wiele prywatnych rzeczy. Aby to złagodzić, stosuje się takie rzeczy, jak idiom PIMPL . Jest to coś w rodzaju „przeciwieństwa” biblioteki tylko nagłówkowej, stara się zminimalizować niezbędną zawartość nagłówka.

Ale aby odpowiedzieć na twoje główne pytanie: jest to kompromis. Im więcej kodu biblioteki umieszcza się w plikach nagłówkowych, tym bardziej kompilator ma szansę zoptymalizować kod pod kątem szybkości (jeśli tak się naprawdę zdarza lub jeśli wzrost jest zauważalny, to zupełnie inne pytanie). Z drugiej strony, zbyt dużo kodu w nagłówkach wydłuża czas kompilacji. Szczególnie w dużych projektach C ++ może to stać się poważnym problemem, patrz „Projektowanie oprogramowania w dużej skali C ++” Johna Lakosa - choć książka jest nieco przestarzała, a niektóre z opisanych tam problemów są rozwiązywane przez nowoczesne kompilatory, ogólne pomysły / rozwiązania są nadal aktualne.

W szczególności, gdy nie używasz stabilnej biblioteki (strony trzeciej), ale opracowujesz własne biblioteki lib podczas projektu, czasy kompilacji stają się widoczne. Za każdym razem, gdy zmieniasz coś w bibliotece, musisz zmienić plik nagłówka, co spowoduje rekompilację i powiązanie wszystkich jednostek zależnych.

IMHO popularność bibliotek tylko nagłówkowych jest spowodowana popularnością szablonów meta-programowania. W przypadku większości kompilatorów biblioteki szablonów muszą być tylko nagłówkami, ponieważ kompilator może uruchomić główny proces kompilacji tylko wtedy, gdy podano parametry typu, a dla pełnej kompilacji i optymalizacji kompilator musi widzieć „oba naraz” - kod biblioteki plus szablon wartości parametrów. Uniemożliwia to (lub przynajmniej utrudnia) tworzenie jakichkolwiek „wstępnie skompilowanych” jednostek kompilacji dla takiej biblioteki.

Doktor Brown
źródło
6
Krótko mówiąc, biblioteki zawierające tylko nagłówki są wygodniejsze niż bardziej wydajne ; a ponieważ C ++ nie ma żadnego standardowego menedżera pakietów, pomaga to przyspieszyć adopcję.
Matthieu M.,
6
@MatthieuM: nie, skompilowany kod może czasami być bardziej wydajny, a dla bibliotek szablonów projektowanie samego nagłówka zazwyczaj nie jest kwestią wygody. Zwiększone czasy kompilacji zdecydowanie nie są wygodniejsze.
Doc Brown
Posiadanie gorącego kodu w nagłówkach może rzeczywiście prowadzić do bardziej wydajnego kompilowanego kodu, jednak nie wymaga to posiadania całego kodu w nagłówkach, a zatem jest niezależny, IMHO, od bibliotek zawierających tylko nagłówki.
Matthieu M.,
1
@ MatthieuM .: to z pewnością poprawne, jednak myślę, że termin „wygodniejszy” nie opisuje dobrze przypadku.
Doc Brown
3
Pracowałem nad dużym, osadzonym projektem, opartym na zmniejszonym, starszym systemie operacyjnym dla mojego poprzedniego pracodawcy, i w tych przypadkach mogę potwierdzić ból związany z długimi czasami kompilacji. Ktoś przyłapany na wrzucaniu zbyt wielu rzeczy do plików nagłówkowych byłby traktowany bezlitośnie.
Fred Thomsen
15

Cóż, najpierw zburzymy niektóre z twoich założeń:

  1. Jedną z zalet bibliotek tylko nagłówkowych dla C ++ jest to, że nie trzeba ich oddzielnie kompilować.

Kompilowanie osobno oznacza potencjalnie brak konieczności ponownej kompilacji wszystkiego, jeśli zmieni się tylko część.
Zatem wada zamiast przewagi.

  1. W C i C ++ wstawianie ma sens tylko wtedy, gdy funkcja jest zdefiniowana w pliku nagłówkowym *.

Tak, jedynym efektem, inlinejaki pozostał, jest wyjątek od reguły jednej definicji .
Biada wam, jeśli te definicje różnią się w jakikolwiek sposób.

Tak więc, jeśli funkcja jest wewnętrzną jednostką kompilacji, zaznacz ją static. To sprawia, że ​​wstawianie jest bardziej prawdopodobne, ponieważ funkcja musi być dostępna w celu jej wstawienia.
Mimo to spójrz na optymalizację czasu łącza, obsługiwaną co najmniej przez MSVC ++, gcc i clang.

  1. Tradycyjnie w C używany jest układ .c / .h, w którym nagłówek reprezentuje minimalny publiczny interfejs jednostki tłumaczeniowej. Podobnie .cpp / hpp.

Cóż, tylko przedstawienie minimalnego interfejsu jest z pewnością jednym z celów, aby osiągnąć wyższą stabilność API i ABI oraz zminimalizować czas kompilacji.

Zwłaszcza klasy C ++ nie są do tego specjalnie dostosowane, ponieważ wszystkie prywatne bity przeciekają do nagłówka, podobnie jak chronione, czy chcesz z nich czerpać, czy nie.

Wzorzec projektowy PIMPL służy do zmniejszenia takich szczegółów.

Jednak częścią, w której interfejs i implementacja całkowicie nie działają w C ++, są szablony.
Komitet próbował zrobić coś z wyeksportowanymi szablonami , ale zostało to porzucone jako zbyt skomplikowane i tak naprawdę nie działające.

Teraz pracują nad odpowiednim systemem modułów , choć działa powoli. To znacznie skraca czas kompilacji, a także powinno zwiększyć stabilność API i ABI poprzez zmniejszenie ich powierzchni.

Czy biblioteki zawierające tylko nagłówki są generalnie bardziej wydajne pod względem kodu i czasu wykonania niż tradycyjny układ? Jeśli tak, to czy wynika to z dużej inlinizacji lub innych optymalizacji?

Biblioteki zawierające tylko nagłówki mogą być bardziej wydajne pod względem rozmiaru kodu i czasu wykonywania, choć zależy to od tego, czy biblioteka jest współużytkowana, ile z niej jest wykorzystywana, w jaki sposób i czy inlining stanowi decydującą wygraną w tym konkretnym przypadku.

Powodem, dla którego wstawianie jest tak ważne dla optymalizacji, nie jest to, że wkładanie samo w sobie jest tak świetnym wzmocnieniem, ale ze względu na możliwości ciągłej propagacji i dalszej optymalizacji, otwiera się.

Deduplikator
źródło