2012-03-16 10 views
8

Avevo un singolo file sorgente che conteneva tutte le definizioni e le funzioni di classe.Eseguibile file di origine multipli più lento dell'eseguibile file sorgente singola

Per un'organizzazione migliore, ho spostato le dichiarazioni di classe (.h) e le implementazioni (.cpp) in file separati.

Ma quando li ho compilati, si è verificato un eseguibile più lento rispetto a quello ottenuto dal file eseguibile single source. I suoi circa 20-30 secondi più lenti per lo stesso input. Non cambio alcun codice.

Perché sta succedendo? E come posso renderlo più veloce di nuovo?

Aggiornamento: L'eseguibile a sorgente singola viene completato in 40 secondi mentre il file eseguibile di origine multipla richiede 60. E mi riferisco al runtime e non alla compilazione.

+3

20-30 secondi per quanto tempo in totale? –

+2

Inoltre, stai collegando/compilando sulla riga di comando, o usando un IDE come VS? Ovviamente ci vorrà più tempo per collegare i file sorgente insieme se tutto fosse in un unico file, ma la differenza di 20-30 secondi è troppo lunga per tutte le applicazioni tranne quelle enormi. Se dovessi indovinare, e questa è solo una supposizione, direi che dovresti diffondere la tua fonte su migliaia di file per ottenere quei tempi di costruzione. – prelic

+1

Sei sicuro che non stai compilando usando i flag "debug"? 20 secondi sono ** un sacco **. – mfontanini

risposta

10

Penso che il tuo programma funzioni più velocemente quando compilato come un singolo file perché in questo caso il compilatore ha più informazioni, necessarie per ottimizzare il codice. Ad esempio, può integrare automaticamente alcune funzioni, il che non è possibile in caso di compilazione separata.

Per rendere ancora più veloce, è possibile provare a abilitare ottimizzatore del collegamento (o l'intero programma di ottimizzazione) con questa opzione: -flto.


Se -flto opzione non è disponibile (ed è disponibile solo a partire con gcc 4.6) o se non si desidera utilizzarlo per qualche ragione, di avere almeno 2 opzioni:

  1. Se si divide solo il progetto for better organization, è possibile creare un singolo file di origine (come all.cxx) e #include tutti i file di origine (tutti gli altri file *.cxx) in questo file. Quindi è necessario creare solo questo all.cxx e tutte le ottimizzazioni del compilatore sono nuovamente disponibili. Oppure, se lo dividi anche per aumentare la compilazione, puoi preparare 2 opzioni di build: build incrementale e build di unità. Il primo costruisce tutte le fonti separate, la seconda - solo all.cxx. Ulteriori informazioni su questo here.
  2. È possibile trovare le funzioni, che costano le prestazioni dopo aver diviso il progetto e spostarle nell'unità di compilazione, dove sono utilizzate o nel file di intestazione. Per fare ciò, iniziare con la creazione di profili (vedere "What can I use to profile C++ code in Linux?"). Indagare ulteriormente su parti del programma che influiscono in modo significativo sulle prestazioni del programma; qui ci sono 2 opzioni: o usate di nuovo il profiler per confrontare i risultati di build incrementali e di unità (ma questa volta avete bisogno di un profiler di campionamento, come oprofile, mentre un profiler di strumentazione, come gprof, molto probabilmente è troppo pesante per questo compito); o applicare una strategia "sperimentale", come descritto da gbulmer.
+0

Pensa anche tu. Probabilmente inlining. –

+0

È applicabile a g ++? – questions

+3

@questions, sì, gcc può fare l'ottimizzazione di tutto il programma e per attivarlo, usa l'opzione '-flto'. gcc e g ++ sono solo 2 nomi diversi per lo stesso compilatore, usati per c o C++ di conseguenza. –

5

Questo probabilmente ha a che fare con link time optimization. Quando tutto il codice si trova in un singolo file sorgente, il compilatore ha più conoscenza di ciò che fa il codice in modo che possa eseguire più ottimizzazioni. Una tale ottimizzazione è inlining: il compilatore può solo inline una funzione se conosce la sua implementazione in fase di compilazione!

Queste ottimizzazioni possono essere eseguite anche al tempo di collegamento (anziché in fase di compilazione) passando il flag -flto in gcc, sia per la fase di compilazione che per la fase di collegamento (vedere here).

+0

Intendevi -flto? È applicabile con g ++? – questions

+1

Sì, errore, mi dispiace. '-flto' funziona altrettanto bene con g ++ - diamine, secondo la manpage funziona anche quando si mescolano i linguaggi. – Thomas

1

questo è un approccio più lento a tornare alla fase di esecuzione più veloce, ma se si voleva ottenere una migliore comprensione di ciò che sta causando il grande cambiamento, si potrebbe fare

Un esperimento alcuni 'esperimenti' sarebbe per trovare quale funzione potrebbe essere responsabile per il grande cambiamento. Per fare ciò, è possibile "profilare" il runtime di ciascuna funzione.

Ad esempio, utilizzare GNU gprof, parte di GNU binutils: http://www.gnu.org/software/binutils/
Documenti a: http://sourceware.org/binutils/docs-2.22/gprof/index.html

Questa misurerà il tempo impiegato da ogni funzione nel vostro programma, e dove è stato chiamato da. Effettuare queste misurazioni probabilmente avrà un "effetto Heisenberg"; prendere le misure influirà sulle prestazioni del programma. Quindi potresti voler provare un esperimento per trovare quale classe stia facendo la differenza.

Cercare di ottenere un'immagine di come il tempo di esecuzione varia tra il codice sorgente della classe nell'origine principale e lo stesso programma, ma con la classe compilata e collegata separatamente.

Per ottenere un'implementazione di classe nel programma finale, è possibile compilarlo e collegarlo, oppure solo # includerlo nel programma "principale", quindi compilare main.

Per rendere più facile per provare permutazioni, si potrebbe passare un #include o disattivare utilizzando #if:

#if defined(CLASSA) // same as #ifdef CLASSA 
#include "classa.cpp" 
#endif 
#if defined(CLASSB) 
#include "classb.cpp" 
#endif 

Quindi è possibile controllare quali file sono #include utilizzando bandiere riga di comando per il compilatore, per esempio

g++ -DCLASSA -DCLASSB ... main.c classc.cpp classd.cpp classf.cpp 

Si potrebbe solo richiedere alcuni minuti per generare le permutazioni delle -Dflags e comandi di collegamento. Allora avresti un modo per generare tutte le permutazioni di compilare 'in una unità' vs separatamente collegate, quindi eseguire (e tempo) ciascuna.

presumo i file di intestazione sono avvolti nel solito

#ifndef _CLASSA_H_ 
#define _CLASSA_H_ 
//... 
#endif 

Si potrebbe quindi avere alcune informazioni sulla classe che è importante.

Questi tipi di esperimento potrebbero fornire alcune informazioni sul comportamento del programma e del compilatore che potrebbero stimolare altre idee per miglioramenti.

Problemi correlati