2015-04-20 8 views
6

Ho un progetto che utilizza un po 'di meta-programmazione di template in C++. Questo rende i tempi di compilazione lunghi. Capisco che non posso avere la torta e mangiarlo anch'io, ma mi piacerebbe sapere alcuni suggerimenti e trucchi su come ridurre i tempi di compilazione. Ho già provato le istanze esplicite e mentre ciò potrebbe aiutare in alcuni casi, molte volte, le istanze sono uniche per una particolare unità di compilazione nel qual caso l'istanziazione esplicita non fa nulla per aiutare. E ora stiamo parlando solo di Clang che fa un buon lavoro. Quando provo questo su G ++, il tempo di compilazione esplode. Per un file, ho rinunciato ad aspettare che si compilasse dopo 45 minuti.Quali sono i colpevoli comuni della lentezza del TMP

  • Ci sono dei colpevoli comuni quando si tratta di meta-programmazione modello, cose che sono note per essere spesso problematiche? Quali tecniche dovrei evitare e cosa dovrei fare invece?
  • Ci sono aree in cui è noto che GCC ha prestazioni peggiori di Clang e c'è un modo per aggirare questo problema?

Uso principalmente tecniche di C++ 11 con vaniglia semplice, non utilizzo Boost MPL o librerie simili.

risposta

0

Nei nostri progetti abbiamo usato solo il pensiero e le prove. Cioè per prima cosa pensiamo "quali sono i modelli complessi qui" e poi abbiamo cercato di isolare questi modelli o rimuoverli o refactoring per vedere se le modifiche al tempo di compilazione. Spesso i tempi di compilazione dei template sono accompagnati da un maggiore utilizzo della memoria, ho persino segnalato un bug di gcc una volta su tale argomento (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54056).

C'è un altro trucco che ha aiutato diverse volte, usando l'opzione -Q della riga di comando (mostra le funzioni che gcc attualmente compila). Per esempio. per il bug linkato sopra era chiaro che gcc rallentava su quei template buggati.

2

Ecco alcuni consigli per quanto riguarda metaprogrammazione con C++ 11 e oltre:

  • Preferisco algoritmi basati espansione variadic. I modelli Variadic vengono generalmente gestiti rapidamente dal compilatore rispetto alla catena O (n) delle istanze di template che di solito sono necessarie per le liste di caratteri e relative al vecchio C++ 98/03. Sono impaziente di vedere le espressioni C++ 1z fold, come un modo per implementare i metafuntcitons della famiglia fold usando l'espansione del pacchetto.

  • catene Evitare di successione: algoritmi basati su eredità ricorsiva di solito eseguono male dal momento che il compilatore dovrebbe monitorare i metadati albero di ereditarietà. Preferisci le chiamate non elaborate al prossimo ::type invece di ereditare direttamente dalla successiva "chiamata" come una scorciatoia per ottenere il membro type.

  • Preferisco eredità Expansion Pack per eredità ricorsiva: Cioè, se si implementa una tupla come cosa, preferiscono

    template<typename... Ts> 
    struct tuple : Ts... 
    

    a

    template<typename Head, typename... Tail> 
    struct tuple : Head, tuple<Tail...> 
    

    dal momento che il compilatore dovrebbe intantiate N sottotipi su quest'ultimo, più l'overhead ereditario descritto al punto precedente.

  • Preferiscono algoritmi basati sulla ricerca del nome: la ricerca dei nomi di solito esegue più rapidamente dell'istanza della maschera ricorsiva.Così, in considerazione di fare qualcosa di simile decltype(declval<T>().f()) per calcolare un tipo, in cui il risultato è nel corretto sovraccarico di f.

+0

Purtroppo, queste sono cose che già facciamo. Alcune cose che ho trovato davvero aumentate in termini di compilazione sono state l'uso di 'std :: make_shared' e' std :: shared_ptr'. Li ho sostituiti con il mio conteggio dei riferimenti utilizzando 'std :: atomic' dato che comunque stavo cancellando il tipo. –

+0

@EmilEriksson E 'possibile utilizzare ancora 'std :: shared_ptr' e non ottenere troppo overhead aggiuntivo http://blog2.emptycrate.com/content/template-code-bloat-revisited-smaller-makeshared~~V~~3rd – lefticus

+0

In questo caso, anche se ho ridotto il numero di 'istanze std :: shared_ptr', avrei ancora troppo molti di loro e' std :: shared_ptr' è più costoso di creare un'istanza della mia implementazione. Ovviamente, è costoso, per una buona ragione, che sia necessaria flessibilità per una classe di librerie standard che verrà utilizzata ovunque. Ma ho un caso d'uso molto specifico e quindi, sono stato in grado di ridurre i tempi di compilazione utilizzando un'altra soluzione. –