Gentoo Logo

Guida all'Ottimizzazione della Compilazione

Indice:

1.  Introduzione

Cosa sono le CFLAGS e le CXXFLAGS?

Le CFLAGS e le CXXFLAGS sono variabili d'ambiente utilizzate per dire al GNU Compiler Collection, gcc, che tipo di switch usare durante la compilazione del codice sorgente. Le CFLAGS sono per il codice scritto in C, mentre le CXXFLAGS sono per il codice scritto in C++.

Possono essere utilizzate per diminuire il numero di messaggi di debug di un programma, aumentare i livelli di avvisi d'errore, e, ovviamente, ottimizzare il codice prodotto. Il manuale di GNU gcc (in inglese, ndt) contiene una lista completa delle opzioni disponibili e i loro scopi.

Come sono utilizzate?

Le CFLAGS e le CXXFLAGS possono essere usate in due modi. Primo, possono essere usate per ciascun programma con i Makefile generati da automake.

Comunque, questo non dovrebbe esser fatto quando si installano pacchetti disponibili nel Portage tree. Invece, impostare le proprie CFLAGS e CXXFLAGS in /etc/portage/make.conf. In questo modo tutti i pacchetti verranno compilati usando le proprie opzioni specificate.

Codice 1.1: CFLAGS in /etc/portage/make.conf

CFLAGS="-march=athlon64 -O2 -pipe"
CXXFLAGS="${CFLAGS}"

Come si può notare, le CXXFLAGS sono impostate per utilizzare tutte le opzioni presenti nelle CFLAGS. Questa è la modalità da seguire praticamente sempre per non sbagliare. Non si dovrebbe mai aver bisogno di specificare opzioni addizionali nelle CXXFLAGS.

Idee Sbagliate

Mentre le CFLAGS e le CXXFLAGS possono essere molto efficaci facendo in modo che il codice sorgente produca binari più piccoli e/o più veloci, possono anche alterare la funzione del proprio codice, aumentare le sue dimensioni, rallentare il suo tempo di esecuzione, o perfino causare fallimenti nella compilazione!

Le CFLAGS non sono pallottole magiche; non faranno automaticamente girare più veloce il proprio sistema o renderanno i propri binari più piccoli per occupare meno spazio su disco. Aggiungere più e più flag nel tentativo di ottimizzare (o "personalizzare" in modo estremo) il proprio sistema è una ricetta sicura per il fallimento. C'è un punto nel quale si raggiungeranno risultati peggiori.

Contrariamente alle vanterie che si possono trovare su internet, le CFLAGS e le CXXFLAGS aggressive sono piuttosto dannose per i propri programmi rispetto ai vantaggi che possono introdurre. Tenere a mente che la ragione per cui esistono le flag al primo posto è perchè sono progettate per essere usate in posti specifici per scopi specifici. Solo perchè una particolare CFLAG è buona per un pezzo di codice questo non significa che va bene per compilare tutto ciò che si vorrà installare sulla propria macchina!

Pronti?

Ora che si è informati di alcuni dei rischi implicati, si può dare un'occhiata a qualche sana, sicura ottimizzazione per il proprio computer. Queste saranno molto utili e si conquisteranno le simpatie degli sviluppatori la prossima volta che si riporterà un problema su Bugzilla. (Gli sviluppatori solitamente richiederanno di ricompilare un pacchetto con CFLAG minimali per vedere se il problema persiste. Ricordare, flag aggressive possono rovinare il codice.)

2.  Ottimizzare

Le basi

Lo scopo che sta dietro all'uso delle CFLAGS e delle CXXFLAGS è creare codice adatto al proprio sistema; dovrebbe funzionare perfettamente finchè è piccolo e veloce, se possibile. Qualche volta queste condizioni sono mutualmente esclusive, così si proverà con combinazioni che si sa che funzionano bene. Idealmente, ci sono le migliori disponibili per qualsiasi architettura della CPU. Successivamente verranno menzionate le flag aggressive in modo da sapere da cosa tenersi in guardia. Non verrà discussa ogni opzione elencata nel manuale di gcc (ce ne sono centinaia), ma verranno coperte le flag di base e più comuni.

Nota: Ogni volta che non si è sicuri su cosa faccia una flag, fare riferimento al capitolo appropriato del manuale di gcc(ndt in inglese). Se si è ancora dubbiosi, provare Google, o controllare lemailing list di gcc.

-march

La prima e la più importante opzione è -march. Essa dice al compilatore che codice dovrà esser prodotto per la propria architettura di processore (o arch); dice che dovrà produrre codice per un certo tipo di CPU. Differenti CPU hanno differenti capacità, supportano differenti insiemi di istruzioni, e hanno differenti modi di eseguire il codice. La flag -march istruirà il compilatore a produrre codice specificatamente per la propria CPU, con tutte le sue capacità, funzionalità, insiemi di istruzioni, caratteristiche, e così via.

Sebbene la variabile CHOST in /etc/portage/make.conf specifichi l'architettura generale usata, dovrebbe essere usata anche -march in modo che i programmi possano essere ottimizzati per il proprio specifico processore. Per i processori x86 e x86-64 (fra gli altri) si dovrebbe utilizzare la flag -march.

Che tipo di CPU si possiede? Per scoprirlo, eseguire il seguente comando:

Codice 2.1: Esaminare informazioni sulla CPU

$ cat /proc/cpuinfo

Ora si vedrà -march in azione. Questo esempio è per un vecchio Pentium III:

Codice 2.2: /etc/portage/make.conf: Pentium III

CFLAGS="-march=pentium3"
CXXFLAGS="${CFLAGS}"

Ecco un altro esempio per un processore AMD a 64-bit:

Codice 2.3: /etc/portage/make.conf: AMD64

CFLAGS="-march=athlon64"
CXXFLAGS="${CFLAGS}"

Se non si è ancora sicuri su quale tipo di CPU in proprio possesso, basta usare -march=native. Quando questa flag è usata, GCC rileverà il proprio processore e imposterà automaticamente le flag appropriate. Tuttavia, questa opzione non dovrebbe essere usata se si intende compilare pacchetti per una CPU diversa!

Quindi se si compilano pacchetti su un computer, ma si intende eseguirli su una macchina differente (per esempio quando si utilizza un computer veloce per compilare pacchetti per un altro più vecchio e lento), allora non usare -march=native. "Native" significa che il codice prodotto girerà solamente su quel tipo di CPU. Le applicazioni compilate con -march=native su una CPU AMD Athlon 64 non potranno essere eseguite su una vecchia CPU VIA C3.

Sono anche disponibili le flag -mcpu e -mtune. Queste sono solitamente usate solo quando non è disponibile nessuna opzione-march; certe architetture di processori possono richiedere -mtune o persino -mcpu. Sfortunatamente, il comportamento di gcc non è molto consistente con il modo in cui si comporta ogni flag da una architettura alla successiva.

Nelle CPU x86 e x86-64, -march genererà codice specifico per quella CPU usando tutti gli insiemi di istruzioni disponibili e la corretta ABI; non avrà retrocompatibilità per CPU più vecchie/differenti. Se non si ha bisogno di eseguire codice su nessun altro sistema al di fuori di quello su cui si sta eseguendo Gentoo, continuare ad usare -march. Si potrebbe considerare l'utilizzo di -mtune solo quando si ha bisogno di generare codice per vecchie CPU come ad esempio i386 e i486. -mtune produce codice più generico di -march; sebbene ottimizzi il codice per una certa CPU, non tiene conto insiemi di istruzioni e ABI. Non utilizzare -mcpu su sistemi x86 o x86-64, perchè è deprecato per quelle architetture.

Solo per le CPU diverse da x86/x86-64 (come Sparc, Alpha, e PowerPC) può essere necessario -mtune o -mcpu invece di -march. Su queste architetture, -mtune/-mcpu si comporteranno talvolta proprio come -march (su x86/x86-64) . . . ma con un differente nome di flag. Ancora, il comportamento di gcc e la denominazione delle flag semplicemente non è consistente attraverso le architetture, quindi assicurarsi di controllare il manuale di gcc per determinare quali utilizzare per il proprio sistema.

Nota: Per le impostazioni di -march/-mtune/-mcpu più suggerite, si prega leggere il capitolo 5 del Manuale di Installazione Gentoo appropriato per la propria architettura. Leggere anche la lista di opzioni specifiche per architettura (in inglese, ndt) del manuale di gcc, così come spiegazioni più dettagliate a proposito delle differenze tra -march, -mcpu, e -mtune.

-O

Un'altra variabile è -O. Questa controlla il livello generale di ottimizzazione. Questo rende il tempo di compilazione più lungo, e può richiedere molta più memoria, specialmente se si aumenta il livello di ottimizzazione.

Ci sono cinque impostazioni di -O : -O0, -O1, -O2, -O3, e -Os. Bisogna usare solo una di queste in /etc/portage/make.conf.

Con l'eccezione di -O0, ciascuna impostazione di -O attiva molte flag addizionali, pertanto assicurarsi di leggere il capitolo del manuale di gcc sulle opzioni di ottimizzazione (in inglese, ndt) per imparare quali flag sono attivate ad ogni livello di -O, così come qualche spiegazione su cosa facciano.

Verrà ora esaminato ogni livello di ottimizzazione:

  • -O0: Questo livello (che è contrassegnato dalla lettera "O" seguita da uno zero) disattiva interamente l'ottimizzazione ed è l'opzione predefinita se nessun livello di -O è specificato nelle CFLAG o nelle CXXFLAGS. Il proprio codice non sarà ottimizzato; ciò non è solitamente desiderato
  • -O1: Questo è il livello di ottimizzazione più basilare. Il compilatore proverà a produrre codice più veloce, più piccolo senza impiegare tanto tempo di compilazione. E' piuttosto basilare, ma dovrebbe fare bene il suo lavoro in ogni situazione.
  • -O2: Un passo avanti da -O1. Questo è il livello di ottimizzazione raccomandato a meno che non si abbiano necessità speciali. -O2 attiverà poche altre flag in aggiunta a quelli attivati da -O1. Con -O2, il compilatore tenterà di incrementare le prestazioni del codice senza comprometterne le dimensioni, e senza impiegare troppo tempo di compilazione.
  • -O3: Questo è il più alto livello di ottimizzazione possibile, e anche il più rischioso. Impiegherà un tempo più lungo a compilare il proprio codice con questa opzione, e infatti non dovrebbe essere usato per tutto il sistema con gcc 4.x. Il comportamento di gcc è cambiato significativamente dalla versione 3.x. Nella 3.x, -O3 aveva mostrato tempi di esecuzione marginalmente più veloci rispetto a -O2, ma non è più il caso con gcc 4.x. Compilando tutti i propri pacchetti con -O3 si otterranno binari più grossi che richiedono più memoria, e incrementerà significativamente il numero di compilazioni fallite o comportamenti dei programmi inaspettati (inclusi errori). Gli svantaggi superano i benefici; ricordare il principio di diminuire i risultati. Usare -O3 non è raccomandato per gcc 4.x.
  • -Os: Questo livello ottimizzerà il proprio codice per le dimensioni. Attiva tutte le opzioni di -O2 che non incrementano la dimensione del codice generato. E' utile per macchine che hanno spazio su disco estremamente limitato e/o hanno CPU con cache di piccole dimensioni. Comunque, può causare alcuni problemi, il che spiega perchè è filtrato da molte delle ebuild nel tree. Usare -Os non è raccomandato.

Come citato precedentemente, -O2 è il livello di ottimizzazione raccomandato. Se le compilazioni dei pacchetti causano errori, assicurarsi di non utilizzare -O3. Come opzione di riserva, provare ad impostare le proprie CFLAGS e CXXFLAGS ad un livello di ottimizzazione più basso, come -O1 o persino -O0 -g2 -ggdb (per riportare errori e trovare possibili problemi) e ricompilare il pacchetto.

-pipe

Una flag utilizzata frequentemente è -pipe. Questa flag attualmente non ha effetto sul codice generato, ma rende il processo di compilazione più veloce. Dice al compilatore di usare le pipe invece dei file temporanei durante le differenti fasi della compilazione, con la conseguenza di un maggior consumo di memoria. Su sistemi con poca memoria, gcc potrebbe interrompere la compilazione generando degli errori; in tal caso non usare questa flag.

-fomit-frame-pointer

Questa è una flag molto comune progettata per ridurre la dimensione del codice generato. E' attivata a tutti i livelli di -O (eccetto -O0) su architetture dove fare così non interferisce con il debug (come su x86-64), ma si può aver bisogno di attivarla aggiungendola alle proprie flag. Benchè il manuale di GNU gcc non specifichi tutte le architetture per le quali è attivato utilizzando -O, si avrà bisogno di attivarlo esplicitamente su x86. Comunque, usare questo flag renderà il debug difficile o impossibile.

In particolare, rende più difficile risolvere problemi di applicazioni scritte in Java, benchè Java non sia l'unico codice interessato dall'utilizzo di questa flag. Così finchè la flag può aiutare, può anche rendere il debug più difficile; i backtrace in particolare saranno inutili. Comunque, Se non si ha intenzione di fare molto debug e non si ha aggiunto nessun'altra CFLAG relativa al debug come -ggdb, allora provare a usare -fomit-frame-pointer.

Importante: Non combinare -fomit-frame-pointer con la flag simile -momit-leaf-frame-pointer. Usare quest'ultima flag è scoraggiato, siccome -fomit-frame-pointer svolge già quel compito propriamente. Ancora, è stato dimostrato che -momit-leaf-frame-pointer influisce negativamente sulla prestazioni del codice.

-msse, -msse2, -msse3, -mmmx, -m3dnow

Queste flag abilitano gli insiemi d'istruzioni SSE, SSE2, SSE3, MMX, e 3DNow! (link in inglese, ndt) per le architetture x86 e x86-64. Queste sono utili principalmente per applicazioni multimediali, giochi, e altri compiti di elaborazione che fanno uso intensivo di operazioni in virgola mobile, benchè esse contengano molti altri miglioramenti matematici. Questi insiemi di istruzioni si trovano nelle CPU più moderne.

Importante: Assicurarsi di controllare se la propria CPU li supporti eseguendo cat /proc/cpuinfo. L'output includerà ogni insieme di istruzioni addizionale supportato. Notare che pni è solo un nome differente per SSE3.

Normalmente non si avrà bisogno di aggiungere ognuna di queste flag a /etc/portage/make.conf finchè si utilizzerà il valore corretto per -march (per esempio, -march=nocona implica -msse3). Qualche eccezione notabile sono le nuove CPU VIA e AMD64 che supportano istruzioni non implicate da -march (come SSE3). Per CPU come queste si avrà bisogno di abilitare flag addizionali dove appropriato dopo aver controllato l'output di cat /proc/cpuinfo.

Nota: Si dovrebbe controllare la lista (in inglese, ndt) delle flag specifiche di x86 e x86-64 per vedere quali di questi insiemi di istruzioni sono attivate dall'appropriata flag del tipo della CPU. Se un'istruzione è elencata, allora non si avrà bisogno di specificarla; sarà attivata usando l'appropriata impostazione di -march.

3.  Domande frequenti (FAQ) Sull'Ottimizzazione

Ma si ottengono prestazioni migliori con -funroll-loops -fomg-optimize!

No, tu pensi solo di averle perchè qualcuno ti ha convinto che più flag ci sono meglio è. Le flag aggressive faranno solo del male alle tue applicazioni quando usate per tutto il sistema. Persino il manuale di gcc(ndt in inglese) dice che usare -funroll-loops e -funroll-all-loops rende il codice più grande e più lento. Ancora per qualche ragione, in associazione con -ffast-math, -fforce-mem, -fforce-addr, e flag simili, continua ad essere molto popolare fra i fanatici della personalizzazione che vogliono averne le più grandi ragioni per vantarsene.

La verità della questione è che sono flag pericolosamente aggressive. Dare una buona occhiata sui Forum Gentoo e su Bugzilla per vedere ciò che fanno queste flag: niente di buono!

Non c'è bisogno di usare queste flag globalmente in CFLAGS o CXXFLAGS. Danneggeranno solamente la prestazioni. Ti faranno sentire come se tu avessi un sistema ad alte prestazioni che fa girare l'ultima tecnologia, ma non faranno altro che gonfiare il tuo codice e rendere i tuoi bug marcati come INVALID o WONTFIX.

Non c'è bisogno di flag pericolose come queste. Non usarle. Impuntarsi sulle basi: -march, -O, e -pipe.

Cosa mi dici a proposito dei livelli di -O più alti di 3?

Qualche utente si vanta di prestazioni migliori ottenute usando -O4, -O9, e così via, ma la verità è che i livelli di -O più alti di 3 non hanno effetti. Il compilatore può accettare CFLAGS come -O4, ma attualmente non ne fa nulla. Effettua solamente le ottimizzazioni per -O3, niente di più.

C'è bisogno di più prove? Esaminare il codice sorgente di gcc :

Codice 3.1: codice sorgente di -O

if (optimize >= 3)
    {
      flag_inline_functions = 1;
      flag_unswitch_loops = 1;
      flag_gcse_after_reload = 1;
      /* Allow even more virtual operators.  */
      set_param_value ("max-aliased-vops", 1000);
      set_param_value ("avg-aliased-vops", 3);
    }

Come si può vedere, ogni valore più alto di 3 è trattato esattamente come -O3.

Cosa mi dici delle flag ridondanti?

Spesso le CFLAGS e le CXXFLAGS che sono attivate a vari livelli di -O sono specificatamente ridondanti in /etc/portage/make.conf. Qualche volta ciò è fatto per ignoranza, ma è anche fatto per evitare il filtraggio o la sostituzione delle flag.

Il filtraggio/sostituzione delle flag è fatto in molte degli ebuild nel Portage tree. E' fatto solitamente perchè i pacchetti falliscono la compilazione a certi livelli di -O, o quando il codice sorgente è troppo sensibile per ogni flag addizionale da usare. L'ebuild filtrerà qualcuna o tutte le CFLAGS e le CXXFLAGS, o può sostituire -O con un livello differente.

Il Manuale degli Sviluppatori di Gentoo (ndt in inglese) descrive dove e come funziona il filtraggio/sostituzione di flag.

E' possibile aggirare il filtraggio di -O elencando in modo ridondante le flag per un certo livello, come -O3, in in modo simile a questo:

Codice 3.2: Specificare CFLAGS ridondanti

CFLAGS="-O3 -finline-functions -funswitch-loops"

Comunque, questa non è una cosa intelligente da fare. Le CFLAGS sono filtrate per una ragione! Quando le flag sono filtrate, significa che non è sicuro compilare un pacchetto con queste flag. Chiaramente, non è sicuro compilare il proprio intero sistema con -O3 se qualcuna delle flag attivate da quel livello causeranno problemi con alcuni pacchetti. Quindi, non provare a "battere in intelligenza" gli sviluppatori che mantengono questi pacchetti. Bisogna fidarsi degli sviluppatori. Il filtraggio delle flag e la loro sostituzione è fatto a vantaggio degli utenti! Se un ebuild specifica flag alternative, allora non provare ad aggirarle.

Si Continuerà ad incappare in problemi quando si compila un pacchetto con flag inaccettabili. Quando si riportano i propri problemi su Bugzilla, le flag usate in /etc/portage/make.conf saranno prontamente visibili e verrà detto di ricompilare senza queste flag. Si può evitare il problema di ricompilare non usando flag ridondanti in primo luogo! Non assumere automaticamente di saperne di più degli sviluppatori.

Cosa mi dici delle LDFLAGS?

Gli sviluppatori Gentoo hanno già impostato delle LDFLAGS elementari e sicure nei profili basilari, pertanto non è necessario cambiarle.

Posso usare le flag per pacchetto?

Avvertenza: L'uso delle flags per pacchetto complicano il debug e il supporto. Assicurati di menzionare nei tuoi bug reportsche fai uso di questa opzione e quali sono i cambiamenti che hai apportato.

Le informazioni su come usare le variabili d'ambiente per pacchetto (CFLAGS incluse) sono descritte nel Manuale di installazione Gentoo, Variabili d'ambiente per pacchetto.

4.  Risorse

Le seguenti risorse sono d'aiuto per capire ulteriormente l'ottimizzazione:



Stampa

Aggiornato il 25 dicembre 2012

La versione originale di questo documento non è più mantenuta

Oggetto: Questa guida fornisce un'introduzione all'ottimizzazione di codice compilato usando CFLAGS e CXXFLAGS sane e sicure. Descrive anche la teoria che sta dietro all'ottimizzazione in generale.

Joshua Saddler
Autore

Marcello Magaldi
Traduzione

Donate to support our development efforts.

Copyright 2001-2014 Gentoo Foundation, Inc. Questions, Comments? Contact us.