Domanda

Di recente, un collega mi ha fatto notare che la compilazione di tutto in un singolo file ha creato un codice molto più efficiente rispetto alla compilazione di file di oggetti separati - anche con l'ottimizzazione del tempo di collegamento attivata . Inoltre, il tempo totale di compilazione per il progetto è diminuito in modo significativo. Dato che uno dei motivi principali per l'utilizzo del C ++ è l'efficienza del codice, questo mi ha sorpreso.

Chiaramente, quando l'archiviatore / linker crea una libreria dai file oggetto o li collega in un eseguibile, anche le semplici ottimizzazioni vengono penalizzate. Nell'esempio seguente, il banale allineamento costa l'1,8% in termini di prestazioni quando viene eseguito dal linker anziché dal compilatore. Sembra che la tecnologia del compilatore dovrebbe essere sufficientemente avanzata per gestire situazioni abbastanza comuni come questa, ma non sta accadendo.

Ecco un semplice esempio usando Visual Studio 2008:

#include <cstdlib>
#include <iostream>
#include <boost/timer.hpp>

using namespace std;

int foo(int x);
int foo2(int x) { return x++; }

int main(int argc, char** argv)
{
  boost::timer t;

  t.restart();
  for (int i=0; i<atoi(argv[1]); i++)
    foo (i);
  cout << "time : " << t.elapsed() << endl;

  t.restart();
  for (int i=0; i<atoi(argv[1]); i++)
    foo2 (i);
  cout << "time : " << t.elapsed() << endl;
}

foo.cpp

int foo (int x) { return x++; }

Risultati della corsa: prestazioni dell'1,8% colpite dall'uso di foo anziché inline foo2 .

$ ./release/testlink.exe  100000000
time : 13.375
time : 13.14

E sì, i flag di ottimizzazione del linker (/ LTCG) sono attivi.

È stato utile?

Soluzione

Non sono uno specialista del compilatore, ma penso che il compilatore abbia molte più informazioni disponibili per ottimizzare in quanto opera su un albero linguistico, al contrario del linker che deve accontentarsi per operare sull'output dell'oggetto, molto meno espressivo del codice visto dal compilatore. Quindi meno sforzi vengono spesi dai team di sviluppo del linker e del compilatore per realizzare ottimizzazioni del linker che potrebbero corrispondere, in teoria, ai trucchi del compilatore.

A proposito, mi dispiace di aver distratto la tua domanda originale nella discussione ltcg. Ora capisco che la tua domanda era un po 'diversa, più preoccupata del tempo di collegamento rispetto alle ottimizzazioni statiche del tempo di compilazione possibili / disponibili.

Altri suggerimenti

Il tuo collega non è aggiornato. La tecnologia è qui dal 2003 (sul compilatore MS C ++): / LTCG . La generazione del time code di collegamento sta affrontando esattamente questo problema. Da quello che so GCC ha questa funzionalità sul radar per il compilatore di prossima generazione.

LTCG non solo ottimizza il codice come funzioni di allineamento tra i moduli, ma in realtà riorganizza il codice per ottimizzare la localizzazione della cache e la ramificazione per un carico specifico, vedere Ottimizzazioni guidate dal profilo . Queste opzioni sono normalmente riservate solo ai build di Release poiché il build può richiedere ore per terminare: collegherà un eseguibile strumentato, eseguirà un caricamento di profiling e quindi collegherà nuovamente con i risultati di profiling. Il link contiene dettagli su ciò che è esattamente ottimizzato con LTCG:

  

Inline & # 8211; Ad esempio, se presente   esiste una funzione A che frequentemente   chiama la funzione B e la funzione B è   relativamente piccolo, quindi guidato dal profilo   le ottimizzazioni incorporeranno la funzione B   nella funzione A.

     

Speculazione di chiamata virtuale & # 8211; Se una   chiamata virtuale o altra chiamata tramite a   puntatore a funzione, spesso target a   una certa funzione, un profilo guidato   l'ottimizzazione può inserire a   chiamata diretta eseguita in modo condizionale a   la funzione di destinazione frequente e   la chiamata diretta può essere incorporata.

     

Allocazione dei registri & # 8211; Ottimizzare con   i dati del profilo risultano migliori   allocazione del registro.

     

Ottimizzazione dei blocchi di base & # 8211; Blocco di base   l'ottimizzazione consente di eseguire comunemente   blocchi di base che vengono eseguiti temporaneamente   all'interno di una determinata cornice da inserire   lo stesso insieme di pagine (località). Questo   minimizza il numero di pagine utilizzate,   minimizzando così l'overhead di memoria.

     

Ottimizzazione dimensioni / velocità & # 8211; funzioni   dove il programma trascorre molto tempo   può essere ottimizzato per la velocità.

     

Layout delle funzioni & # 8211; Sulla base della chiamata   grafico e profilo / chiamante chiamato   comportamento, funzioni che tendono ad essere   lungo lo stesso percorso di esecuzione sono   inserito nella stessa sezione.

     

Ottimizzazione del ramo condizionale & # 8211; Con   le sonde di valore, guidate dal profilo   le ottimizzazioni possono trovare se un dato   viene utilizzato il valore in un'istruzione switch   più spesso di altri valori. Questo   il valore può quindi essere estratto da   istruzione switch. Lo stesso si può fare   con istruzioni if ??/ else dove   l'ottimizzatore può ordinare if / else così   che sia il blocco if oppure else   posizionato per primo a seconda del blocco   è più spesso vero.

     

Separazione del codice morto & # 8211; Codice che è   non chiamato durante la profilazione viene spostato   a una sezione speciale che viene aggiunta   fino alla fine del set di sezioni.   Questo mantiene efficacemente questa sezione   dalle pagine usate di frequente.

     

Separazione codice EH & # 8211; Il codice EH,   essendo eccezionalmente eseguito, can   spesso viene spostato in una sezione separata   quando le ottimizzazioni guidate dal profilo possono   determinare che si verificano eccezioni   solo a condizioni eccezionali.

     

Memory Intrinsics & # 8211; L'espansione di   i intrinseci possono essere decisi meglio se   può essere determinato se un intrinseco è   chiamato frequentemente. Una lattina intrinseca   anche essere ottimizzato in base al blocco   dimensione delle mosse o delle copie.

Il tuo collega è più intelligente della maggior parte di noi. Anche se all'inizio sembra un approccio approssimativo, il progetto in linea in un singolo file .cpp ha una cosa che gli altri approcci come l'ottimizzazione del tempo di collegamento non ha e non avrà per un po '- affidabilità

Tuttavia, me lo hai chiesto due anni fa e testimonio che da allora sono cambiate molte cose (almeno con g ++). La devirtualizzazione è molto più affidabile, ad esempio.

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