Domanda

Prima di tutto, io sono non alla ricerca di un modo per forzare il compilatore inline l'attuazione di ogni funzione.

Per ridurre il livello di risposte sbagliate assicurarsi di aver compreso ciò che la parola chiave inline significa in realtà. Ecco buona descrizione, in linea vs statico vs extern .

Quindi la mia domanda, perché non segnare ogni funzione definizione inline? cioè Idealmente, l'unica unità di compilazione sarebbe main.cpp. O forse qualcuno in più per le funzioni che non possono essere definiti in un file di intestazione (Pimpl, ecc).

La teoria dietro questa richiesta strana è che darebbe le informazioni ottimizzatore massima con cui lavorare. Potrebbe inline implementazioni di funzione, naturalmente, ma potrebbe anche fare l'ottimizzazione "cross-modulo" in quanto v'è un solo modulo. Ci sono altri vantaggi?

È uno provato questo con un'applicazione reale? Ha fatto l'incremento di prestazioni? diminuire?!?

Quali sono gli svantaggi di marcatura tutte definizioni di funzioni inline?

  • Compilation potrebbe essere più lenta e si consumano molto di più memoria.
  • iterativo si basa sono rotti, dovrà essere ricostruita dopo ogni cambio l'intera applicazione.
  • LINK volte potrebbero essere astronomici

Tutte queste svantaggio effettuare solo lo sviluppatore. Quali sono gli svantaggi di runtime?

È stato utile?

Soluzione

Ti è #include tutto significa veramente? Ciò darà un solo modulo e lasciare che l'ottimizzatore vedere l'intero programma in una sola volta.

In realtà, di Microsoft Visual C ++ fa esattamente questo quando si utilizza il /GL (intero programma Optimization) interruttore , ma in realtà non compilare nulla fino a quando viene eseguito il linker e ha accesso a tutto il codice. Altri compilatori hanno opzioni simili.

Altri suggerimenti

SQLite utilizza questa idea. Durante lo sviluppo si avvale di una struttura tradizionale fonte. Ma per l'uso effettivo c'è un enorme file C (112K righe). Lo fanno per la massima ottimizzazione. Rivendica un miglioramento delle prestazioni del 5-10%

http://www.sqlite.org/amalgamation.html

(e alcune altre società di gioco) ha provato tramite la produzione di un'uber-cpp che #includeed tutti gli altri; si tratta di una tecnica nota. Nel nostro caso, non sembra influenzare runtime molto, ma gli svantaggi fase di compilazione si Menzione si è rivelata assolutamente paralizzante. Con una mezz 'ora di compilazione dopo ogni singola modifica, diventa impossibile per scorrere in modo efficace. (E questo è con l'applicazione divvied fino in oltre una dozzina di librerie diverse.)

Abbiamo provato a fare una configurazione diversa tale che avremmo avuto più .objs durante il debug e quindi avere l'uber-CPP solo nella release-opt costruisce, ma poi incontrato il problema del compilatore semplicemente a corto di memoria. Per un numero sufficientemente ampio di app, gli strumenti semplicemente non sono fino alla compilazione di un file cpp multimilionario linea.

Abbiamo provato LTCG pure, e che ha fornito una piccola ma bella spinta fase di esecuzione, nei rari casi in cui non ha semplicemente in crash durante la fase di collegamento.

Domanda interessante! Lei è certamente giusto che tutti gli inconvenienti elencati sono specifici per lo sviluppatore. Vorrei suggerire, tuttavia, che uno sviluppatore svantaggiati è molto meno probabilità di produrre un prodotto di qualità. Ci possono essere svantaggi di runtime, ma immaginare come riluttanti uno sviluppatore sarà quello di fare piccoli cambiamenti se ogni compilazione richiede ore (o giorni pari) per completare.

Vorrei guardare a questo da un punto di "ottimizzazione prematura": il codice modulare in più file rende la vita più facile per il programmatore, per cui v'è un ovvio beneficio a fare le cose in questo modo. Solo se una specifica applicazione risulta a correre troppo lento, e si può dimostrare che inlining tutto ha un miglioramento misurato, avrei anche considerare scomodare gli sviluppatori. Anche allora, sarebbe dopo una maggioranza di sviluppo è stato fatto (in modo che possa essere misurata) e probabilmente essere fatto solo per la produzione di costruisce.

Questa è semi-correlati, ma nota che Visual C ++ ha la capacità di fare l'ottimizzazione cross-moduli, tra cui in linea attraverso i moduli. Vedere http://msdn.microsoft.com/en- us / library / 0zza0de8% 28VS.80% 29.aspx per informazioni.

Per aggiungere una risposta alla tua domanda iniziale, non credo che ci sarebbe un aspetto negativo in fase di esecuzione, assumendo l'ottimizzatore era abbastanza intelligente (da qui il motivo per cui è stata aggiunta come opzione di ottimizzazione in Visual Studio). Basta usare un compilatore abbastanza intelligente per farlo automaticamente, senza creare tutti i problemi di cui parli. :)

Little beneficio Su un buon compilatore per una piattaforma moderna, inline interesserà solo pochissime funzioni. E 'solo un suggerimento al compilatore, i compilatori moderni sono abbastanza bravo a prendere questa decisione se stessi, e il il sovraccarico di una chiamata di funzione è diventato piuttosto piccolo (spesso, il principale vantaggio di inline non è a ridurre chiamata sovraccarico, ma aprendo ulteriori perfezionamenti).

momento della compilazione Tuttavia, dal momento che in linea cambia anche la semantica, si dovrà #include tutto in un unico grande unità di compilazione. Questo di solito aumenta il tempo di compilazione in modo significativo, il che è un killer su grandi progetti.

Codice dimensione
se ci si allontana da piattaforme desktop attuali e dei suoi compilatori ad alte prestazioni, le cose cambiano molto. In questo caso, la dimensione del codice aumentata generato da un compilatore meno intelligente sarà un problema - tanto che rende il codice molto più lento. Sulle piattaforme embedded, la dimensione del codice è di solito la prima restrizione.

Ancora, alcuni progetti può e fare profitto da "inline tutto". Ti dà lo stesso effetto di ottimizzazione dei tempi di collegamento, almeno se il compilatore non segue ciecamente la inline.

E 'fatto già in alcuni casi. E 'molto simile al concetto di costruisce , ed i vantaggi e svantaggi, non fa da sono quello che si descibe:

  • più potenziale per il compilatore di ottimizzare
  • tempo collegamento va fondamentalmente via (se tutto è in una singola unità di traduzione, non c'è nulla da collegamento, in realtà)
  • tempo di compilazione va, bene, in un modo o l'altro. Incrementale costruisce diventare impossibile, come lei ha ricordato. D'altra parte, una build completa sta per essere più veloce di quanto sarebbe altrimenti (come ogni riga di codice viene compilato esattamente una volta. In un accumulo regolare, il codice nelle intestazioni finisce per essere compilato in ogni unità di traduzione in cui è incluso l'intestazione )

Ma nei casi in cui si dispone già di un sacco di codice header-solo (ad esempio se si utilizza un sacco di Boost), potrebbe essere un'ottimizzazione molto utile, sia in termini di tempo di costruzione e le prestazioni eseguibile.

Come sempre, però, quando le prestazioni sono coinvolti, dipende. Non è una cattiva idea, ma non è universalmente applicabile sia.

Per quanto riguarda il tempo buld va, avete fondamentalmente due modi per ottimizzare esso:

  • ridurre al minimo il numero di unità di traduzione (in modo che le intestazioni sono compresi nel minor numero di posti), o
  • minimizzare la quantità di codice nelle intestazioni (in modo che il costo di includere un'intestazione in più unità di traduzione diminuisce)

codice C richiede in genere la seconda opzione, più o meno alla sua estrema: quasi nulla a parte le dichiarazioni previsionali e le macro sono tenuti nelle intestazioni. C ++ si trova spesso intorno alla metà, che è dove si ottiene il peggior tempo di costruzione totale possibile (ma PCH del e / o incrementale costruisce può radere un periodo di pausa di nuovo), ma di andare avanti nella direzione opposta, riducendo al minimo il numero di unità di traduzione può davvero fare miracoli per il tempo totale di generazione.

Questo è più o meno la filosofia intero programma di ottimizzazione e Link Time Code Generation (LTCG):. opportunità di ottimizzazione sono migliori con conoscenza globale

Da un punto di vista pratico si tratta di una sorta di dolore perché ora ogni singola modifica apportata richiederà una ricompilazione dell'intero albero dei sorgenti. In generale è necessario una build ottimizzata meno frequentemente di quanto è necessario apportare modifiche arbitrarie.

Ho provato questo in epoca Metrowerks (è abbastanza facile da installare con un "Unità" costruire style) e la compilazione mai finito. Cito solo per sottolineare che si tratta di una configurazione del flusso di lavoro che è probabile che tassare la toolchain in modi che non si aspettavano.

Il presupposto è che il compilatore non può ottimizzare tra le varie funzioni. Che è una limitazione di compilatori specifici e non un problema generale. Usando questo come una soluzione generale per un problema specifico potrebbe essere male. Il compilatore può benissimo appena gonfiare il tuo programma con quello che avrebbe potuto funzioni riutilizzabili allo stesso indirizzo di memoria (sempre per usare la cache) in fase di compilazione altrove (e di perdere le prestazioni a causa della cache).

funzioni grandi in termini di costi generali di ottimizzazione, v'è un equilibrio tra il sovraccarico di variabili locali e la quantità di codice nella funzione. Mantenendo il numero di variabili nella funzione (sia passati a, locale e globale) entro il numero di variabili monouso per i risultati della piattaforma in quasi tutto poter rimanere in registri e non devono essere trasferiti per ram, anche una pila telaio non è necessaria (dipende dalla destinazione) in modo funzione di chiamata sovraccarico è notevolmente ridotta. Difficile da fare in applicazioni reali per tutto il tempo, ma l'alternativa di un piccolo numero di grandi funzioni con un sacco di variabili locali il codice sta per spendere una quantità significativa di sfratto e di carico registri di tempo con le variabili da / ram (dipende dalla target).

Prova LLVM è in grado di ottimizzare tutto l'intero programma non solo la funzione per funzione. Uscita 27 aveva raggiunto a ottimizzatore di gcc, almeno per un test o due, ho fatto fare test approfonditi delle prestazioni. E 28 è fuori quindi suppongo che sia meglio. Anche con alcuni file il numero di combinazioni di sintonia manopola sono troppi a pasticciare con. Trovo meglio non ottimizzare a tutti fino ad avere l'intero programma in un unico file, quindi eseguire l'ottimizzazione, dando l'ottimizzatore l'intero programma di lavoro con, in pratica ciò che si sta cercando di fare con inlining, ma senza il bagaglio.

foo() Supponiamo e bar() sia chiamata qualche helper(). Se tutto è in un'unità di compilazione, il compilatore potrebbe scegliere di non inline helper(), al fine di ridurre le dimensioni totali di istruzioni. Questo cause foo() per effettuare una chiamata di funzione non inline a helper().

Il compilatore non sa che un miglioramento del nanosecondo per il tempo di esecuzione di foo() aggiunge $ 100 / giorno per la vostra linea di fondo in attesa. Non sapere che un miglioramento delle prestazioni o il degrado di qualcosa al di fuori di foo() non ha alcun impatto sulla vostra linea di fondo.

Solo tu come il programmatore sa queste cose (dopo un'attenta profilazione ed analisi, naturalmente). La decisione di non bar() linea è un modo per dire al compilatore quello che sai.

Il problema con inlining è che si desidera funzioni ad alte prestazioni per adattarsi nella cache. Si potrebbe pensare che la funzione di chiamata in testa è il grande colpo di prestazioni, ma in molte architetture di cache miss farà esplodere le spinte di coppia e pop fuori dall'acqua. Ad esempio, se si dispone di una funzione di grandi dimensioni (forse di profondità) che deve essere chiamato molto raramente dal principale percorso ad alte prestazioni, potrebbe causare il ciclo principale ad alte prestazioni per crescere al punto in cui non si adatta in L1 Icache. Questo rallenterà il codice in basso molto, molto più che la chiamata di funzione occasionale.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top