Jak radzisz sobie z coraz dłuższymi czasami kompilacji podczas pracy z szablonami?

13

Korzystam z programu Visual Studio 2012, a on ma przypadki, w których dodaliśmy parametry szablonów do klasy „tylko” w celu wprowadzenia „punktu szwu”, aby w teście jednostkowym mogliśmy zastąpić te części próbnymi obiektami.

Jak zwykle wprowadzasz punkty szwu w C ++: używając interfejsów i / lub mieszając na podstawie niektórych kryteriów z interfejsami niejawnymi, używając również parametrów szablonów? Jednym z powodów, dla których warto o to zapytać, jest to, że czasami podczas kompilacji pojedynczy plik C ++ (zawierający pliki szablonów, które mogą również zawierać inne szablony) powoduje wygenerowanie pliku obiektowego, który zajmuje około 5-10 sekund na komputerze programisty .

O ile rozumiem, kompilator VS nie jest szczególnie szybki w kompilowaniu szablonów, a także ze względu na model włączenia szablonów (praktycznie dołączasz definicję szablonu do każdego pliku, który używa go pośrednio i być może tworzysz go ponownie za każdym razem, gdy modyfikujesz coś, co nie ma nic wspólnego z tym szablonem) możesz mieć problemy z czasami kompilacji (podczas kompilacji przyrostowej).

Jakie są twoje sposoby obsługi przyrostowego (i nie tylko) czasu kompilacji podczas pracy z szablonami (oprócz lepszego / szybszego kompilatora :-)).

Ghita
źródło
1
Wstrzykiwanie zależności @RobertHarvey odbywa się przy użyciu parametrów szablonu. W kodzie produkcyjnym, w którym tworzę je, mam wolne czasy kompilacji.
Ghita
5
Czy używasz C ++ 11? patrz en.wikipedia.org/wiki/C%2B%2B11#Extern_template
mike30
2
Ponieważ Andrei Alexandrescu napisał „Modern design C ++”, wielu programistów C ++ uważa, że ​​muszą używać szablonów do wszystkiego i pozwolić kompilatorowi obsługiwać jak najwięcej. Zwykle prowadzi to do opisywanych efektów. Dawniej (i obecnie nadal dla programistów używających innych języków) absolutnie dobrze było nie używać szablonów i obsługiwać takich rzeczy jak wstrzykiwanie zależności za pomocą mechaniki czasu wykonywania, nawet gdy wymaga to więcej cykli procesora dla użytkownika końcowego (czego prawie nigdy nie zauważy ). Szczerze mówiąc, jestem pewien, że Robert ma 100% racji i tak właśnie o tym myślisz.
Doc Brown
1
@Ghita: IMHO przy użyciu meta programowania szablonów jest często tylko formą przedwczesnej optymalizacji (a czasem po prostu nadmiernej wydajności) - o ile nie piszesz bibliotek takich jak STL o porównywalnych wymaganiach. Wymieniasz pewien wzrost wydajności na dłuższe czasy kompilacji, mniej konserwacji i wiele trudnych do zrozumienia komunikatów o błędach. Korzystanie z „zewnętrznych szablonów” może teraz pomóc krótkoterminowo, ale gdybym był w twoich butach, pomyślałbym również o długoterminowych ulepszeniach.
Doc Brown
4
@DocBrown. I odwrotnie, można powiedzieć, że unikanie szablonów w celu poprawy wydajności kompilacji to przedwczesna optymalizacja. Szablony są idealnymi abstrakcjami dla wielu problemów.
mike30

Odpowiedzi:

9

Jeśli parametry szablonów mogą przyjmować tylko skończony (i mały) zestaw wartości, możesz przenieść ich definicję do pliku źródłowego i użyć jawnej instancji .

Na przykład, aaa.hdeklarujesz tylko funkcje szablonu fi g:

template <int n>
int f();

template <class T>
void g(int a);

Zakładamy nszablonu parametr może być tylko 1, 3, 6 i Tparametr szablonu może być tylko int, longi void *.

Następnie definiujesz je w aaa.cppnastępujący sposób:

template <int n>
int f()
{
    ...
}

template <class T>
void g(int a)
{
    ...
}

template int f<1>();
template int f<3>();
template int f<6>();

template void g<int>(int a);
template void g<long>(int a);
template void g<void *>(int a);

W ten sposób kompilator tworzy instancję szablonu dla podanych parametrów podczas kompilacji aaa.cpp. Podczas kompilowania kodu klienta zakłada się, że gdzieś istnieją definicje, a linker się tym zajmie.

#include "aaa.h"

int main()
{
    f<1>();
    f<3>();
    f<6>();

    g<int>(5);
    g<long>(5);
    g<void *>(5);
}

Możesz także jawnie tworzyć instancje klas szablonów. Wadą jest to, że nie można użyć flub gz innymi parametrami szablonu.

#include "aaa.h"

int main()
{
    f<5>();
}

prowadzi do

undefined reference to `int f<5>()'

Zastosowałem tę technikę w projekcie, w którym kilka złożonych klas zależało od małego (<10) zestawu parametrów szablonu liczb całkowitych, co znacznie skróciło czas kompilacji (ponieważ kompilator nie musiał analizować złożonych definicji szablonów podczas kompilowania kodu klienta) . Oczywiście możesz uzyskać mniejsze ulepszenia, w zależności od rzeczywistego kodu.

Claudio
źródło
2

Kiedyś użyłem dziwnego rozwiązania podobnego problemu: Uwzględnienie odprowadzenia STL do czasów kompilacji, takich jak kilka sekund na plik źródłowy - bez względu na to, jak mały. Więc zawarłem wszystkie moje pliki źródłowe w jednym pliku głównym, a czas kompilacji na plik prawie się nie zmienił ... co oznaczało przyspieszenie 20+, ponieważ miałem tylko jeden plik do skompilowania.

Aby utrzymać projekt w czystości, nadal utrzymywałem plik makefile, ale nigdy go nie użyłem (z wyjątkiem weryfikacji, czy nadal działa).

maaartinus
źródło
0

Kiedyś odpalaliśmy duże zadanie, aby skompilować nasze wstępnie skompilowane nagłówki i szablony w ciągu jednej nocy, i po prostu zbudować z tymi następnego dnia.

Ti Strga
źródło