GCC optimization/it

Questa guida costituisce un'introduzione all'ottimizzazione del codice compilato utilizzando valori sicuri per le variabili CFLAGS e CXXFLAGS. Inoltre viene descritta la teoria a fondamento dell'ottimizzazione in generale.

Cosa sono CFLAGS e CXXFLAGS?
CFLAGS e CXXFLAGS sono variabili d'ambiente che vengono utilizzate per comunicare alla GNU Compiler Collection quali sono le opzioni da attivare quando viene compilato del codice sorgente. Le CFLAGS si riferiscono al codice sorgente scritto in C mentre le CXXFLAGS si riferiscono al codice sorgente scritto in C++.

Possono essere usate per ridurre la quantità di messaggi per il debug, per incrementare i livelli dei messaggi di avvertimento e ovviamente per ottimizzare il codice prodotto. Il manuale GCC contiene una lista completa delle opzioni disponibili, e per ciascuna di esse viene fornita una spiegazione del motivo per cui vengono usate.

Come vengono usate?
Le CFLAGS e le CXXFLAGS possono essere utilizzate in due modi. Il primo modo consiste nell'utilizarle a livello di programmi singoli con i Makefile generati da automake.

Tuttavia ciò non dovrebbe essere fatto quando si installano pacchetti di Portage. È preferibile invece impostare le CFLAGS e le CXXFLAGS nel file. In questo modo tutti i pacchetti vengono compilati usando le stesse impostazioni.

CFLAGS in /etc/portage/make.conf

Come è possibile vedere le CXXFLAGS sono state impostate con le stesse opzioni presenti nelle CFLAGS. Si tratta della configurazione più sicura, e in genere non dovrebbe mai sorgere la necessità di specificare opzioni diverse per le CXXFLAGS.

Convinzioni errate
Anche se le CFLAGS e le CXXFLAGS possono essere un modo molto valido per ottenere codice binario più piccolo o più veloce esse, se utilizzate in modo errato, possono anche compromettere la funzionalità del codice stesso, aumentare a dismisura le sue dimensioni, ridurre drasticamente le prestazioni o anche provocare errori di compilazione!

Le CFLAGS non sono una panacea ad ogni male: non rendono sempre il sistema dell'utente più veloce e non riducono sempre le dimensioni del codice binario. Aggiungere sempre più flag nell'intento di ottimizzare sempre più il sistema conduce sicuramente al fallimento. C'è un punto oltre al quale si ottengono miglioramenti ogni volta più scarsi.

A dispetto di certe informazioni che è possibile trovare su Internet, CFLAGS e CXXFLAGS aggressive fanno solitamente più male che bene alla maggior parte dei programmi. Occorre tenere presente che molte opzioni sono state progettate per essere usate in situazioni particolari e per ragioni specifiche. Il fatto che una certa opzione sia positiva per una determinata porzione di codice non significa che tale opzione sia per questo adatta ad essere utilizzata globalmente per compilare tutti i programmi del sistema!

Pronto?
Adesso che il lettore è stato informato dei possibili rischi è possibile esaminare alcune ottimizzazioni sicure, utilizzando le quali si ha l'approvazione degli sviluppatori quando si segnala un problema su Bugzilla. Gli sviluppatori di solito richiedono all'utente di ricompilare il pacchetto problematico con un insieme ridotto di CFLAGS per vedere se il problema persiste. Si ricordi che opzioni aggressive possono rovinare il codice.

Le basi
Lo scopo che ci si pone utilizzando le CFLAGS e le CXXFLAGS è di creare codice su misura per il sistema dell'utente; tale sistema dovrebbe funzionare perfettamente ed essere anche snello e veloce, se possibile. Alcune volte queste ultime due condizioni si escludono a vicenda, e pertanto ci si limiterà ad utilizzare una combinazione nota per funzionare molto bene. Idealmente si tratta della migliore combinazione per ogni architettura di CPU. Verrà fatta menzione delle opzioni aggressive più tardi in modo tale che l'utente possa sapere cosa bisogna evitare. Non verrà discussa ogni opzione presente nel manuale di  (ce ne sono centinaia) ma verranno spiegate solo le opzioni fondamentali e quelle più comuni.

-march
La più importante opzione è. Questa comunica al compilatore di produrre codice per una certa architettura di processori. CPU diverse possiedono diverse funzionalità, supportano diversi insiemi di istruzioni e hanno diverse modalità di eseguire il codice. L'opzione  indica al compilatore di produrre codice specifico per una data CPU, considerando tutte le sue funzionalità, gli insiemi di istruzioni e le modalità di eseguire il codice.

Anche se la variabile CHOST nel file specifica già l'architettura generica da utilizzare,   dovrebbe comunque essere utilizzata anch'essa in modo da ottimizzare il codice per uno specifico processore. In particolare sono le CPU x86 e x86-64 che dovrebbero utilizzare l'opzione.

Quale CPU si possiede? Per scoprirlo, si esegua il seguente comando:

Questo è un esempio per un vecchio Pentium III:

/etc/portage/make.conf: Pentium III

Questo è invece un esempio per una CPU AMD a 64-bit:

/etc/portage/make.conf: AMD64

Se non si è sicuri di sapere quale CPU si possiede ci si può semplicemente limitare ad utilizzare l'opzione. Con questa opzione GCC determina automaticamente di quale processore si dispone e imposta di conseguenza le opzioni più appropriate. Questa opzione, tuttavia, non dovrebbe essere utilizzata qualora si intenda compilare pacchetti per una CPU differente!

Se il lettore sta compilando pacchetti su un certo computer ma intende eseguirli su un computer diverso (ad esempio nel caso in cui si dispone di un computer veloce che compila pacchetti da utilizzare su un'altro computer più lento) non bisogna utilizzare l'opzione. Native significa che il codice prodotto da una certa CPU potrà essere eseguito solo su quel tipo di CPU. Le applicazioni compilate con  su una CPU AMD Athlon 64 non potranno essere eseguite su una vecchia CPU VIA C3.

Esistono anche le opzioni  e. Esse sono normalmente utilizzate quando l'opzione  non è disponibile; i processori di alcune architetture possono richiedere   o addirittura. Sfortunatamente il comportamento di  non è molto consistente se ci si sposta da un'architettura ad un'altra.

Per CPU x86 o x86-64  genera codice specifico per una data CPU usando gli insiemi di istruzioni disponibili e la corretta ABI; non viene garantita la compatibilità con CPU diverse o con CPU più vecchie. Se non si necessita di eseguire codice su un sistema diverso da quello sul quale è presente Gentoo si può continuare ad utilizzare. Il lettore dovrebbe considerare l'utilizzo di  solamente se abbia la necessità di generare codice per CPU quali i386 o i486. produce codice più generico che nel caso di ; anche se ottimizza il codice per una certa CPU non prende in considerazione gli insiemi di istruzioni o l'ABI disponibile. Non utilizzare  su sistemi x86 o x86-64 in quanto è obsoleta su queste architetture.

Solo CPU diverse da x86/x86-64 (come Sparc, Alpha e PowerPC) possono richiedere  o   invece che. Su queste architetture  e   si comportano a volte allo stesso modo di   su architettura x86/x86-64, e cambia solo il nome. Si ricordi ancora una volta che il comportamento di  non è consistente su tutte le architetture, e che pertanto occorre controllare il manuale per determinare quali opzioni bisogna utilizzare sul proprio sistema.

-O
La prossima variabile da considerare è. Essa permette di controllare il livello complessivo di ottimizzazione. Con  la compilazione del codice richiede un po' più di tempo e potrebbe richiedere molta più memoria, specialmente se si innalza il livello di ottimizzazione.

Esistono sette possibili impostazioni per : ,  ,  ,  ,  ,   e. Occorre scegliere solo una tra le precendenti impostazioni e applicarla nel file.

Con la sola eccezione di, ciascuna delle impostazioni   attiva alcune opzioni aggiuntive. Assicurarsi quindi di leggere il capitolo del manuale GCC sulle opzioni per l'ottimizzazione per capire quali opzioni vengono attivate per ciascuno dei livelli  e qual'è la loro funzione.

Esaminiamo assieme ciascuno dei livelli di ottimizzazione:


 * : Si noti che si tratta della lettera "O" seguita da uno zero. Questo livello disattiva del tutto l'ottimizzazione del codice ed è la scelta predefinita qualora nessuna opzione  venga specificata tra le CFLAGS o le CXXFLAGS.   riduce i tempi per la compilazione e può migliorare le informazioni per il debug, ma alcune applicazioni potrebbero non funzionare correttamente se l'ottimizzazione è disattivata completamente. Questa opzione non è pertanto consigliata, a meno che la si scelga per effettuare il debug delle applicazioni.


 * : Questo è il livello di ottimizzazione base. Con  il compilatore prova a produrre codice più veloce e più snello senza richiedere troppo tempo per la compilazione. Si tratta di una opzione piuttosto basilare, ma dovrebbe funzionare in qualsiasi circostanza.


 * : È il passo successivo rispetto a . Si tratta del livello di ottimizzazione "raccomandato" a meno di esigenze particolari.   attiva alcune ulteriori opzioni rispetto a quelle utilizzate da  . Con   il compilatore prova ad incrementare le prestazioni del codice prodotto senza richiedere troppo tempo per la compilazione.


 * : Questo è il più alto livello di ottimizzazione possibile. Vengono attivate ottimizzazioni che sono costose dal punto di vista del tempo di compilazione e dell'utilizzo di memoria. Compilare con  non è un modo sicuro di migliorare le prestazioni del codice; in molti casi si ottiene il rallentamento del sistema a causa del codice binario più grande e dell'utilizzo maggiore di memoria.   è anche nota per causare la corruzione di alcuni pacchetti. Per queste ragioni l'utilizzo di   non è consigliato.


 * : Questa opzione ottimizza il codice dal punto di vista delle sue dimensioni. Essa attiva tutte le opzioni di  che non causano l'incrementano delle dimensioni del codice prodotto.   può essere utile per macchine che hanno una capacità di archiviazione su disco estremamente limitata o CPU con cache dalla dimensione ridotta.


 * : Questa opzione è stata introdotta con GCC 4.8. Essa soddisfa il bisogno di ridurre i tempi della compilazione e quello di migliorare la capacità di effettuare il debug mantenendo però un ragionevole livello prestazionale in fase di esecuzione. Complessivamente con  l'attività di sviluppo dovrebbe risultare migliore rispetto a  . Si noti che   non implica  ;   si limita semplicemente a disattivare le ottimizzazioni che hanno ripercussioni negative sull'attività di debug.


 * : Si tratta di un'opzione introdotta con GCC 4.7. Essa consiste della somma di  con ,   e  .   viola la conformità stretta agli standard e pertanto non è consigliata.

Come menzionato precedentemente  è il livello di ottimizzazione consigliato. Se un pacchetto crea problemi in fase di compilazione e non si sta utilizzando  si riprovi attivando questa opzione. In alternativa provare ad impostare le variabili CFLAGS e CXXFLAGS ad un livello di ottimizzazione più basso, come ad esempio  o addirittura.

-pipe
Un'opzione piuttosto comune è. Essa non ha effetti sul codice generato ma rende più rapido il processo di compilazione. comunica al compilatore di utilizzare pipe al posto di file temporanei durante le varie fasi della compilazione, con la conseguenza di richiedere maggiore memoria. Tale opzione potrebbe causare problemi su sistemi con una disponibilità bassa di memoria perché GCC potrebbe venire terminato. In questo caso si consiglia di non utilizzare questa opzione.

-fomit-frame-pointer
Questa è un'opzione molto comune che ha lo scopo di ridurre le dimensioni del codice generato. Essa viene attivata a tutti i livelli  (eccetto  ) su quelle architetture, quali x86-64, per le quali questa scelta non ha ripercussioni sull'attività di debug. Il manuale di  non spiega su quali architetture   implichi , ma è noto che questa debba essere attivata esplicitamente su x86. In questo caso però il debug del codice diventa difficile o addirittura impossibile da svolgersi.

In particolare  rende molto più difficile l'attività di diagnostica delle applicazioni scritte in Java, ma questo non è l'unico linguaggio interessato. Inoltre attivando  l'attività di debug attraverso le backtrace è impossibile da svolgersi in quanto esse sono del tutto inutilizzabili. Ad ogni modo questa opzione può essere utilizzata se non si ha la necessità di svolgere il debug del software e non si è scelto di attivare opzioni per il debug quale ad esempio.

-msse, -msse2, -msse3, -mmmx, -m3dnow
Queste opzioni attivano sulle architetture x86 e x86-64 gli insiemi di istruzioni SSE, SSE2, SSE3, MMX e 3DNow!. Tali opzioni sono utili principalmente in ambito multimediale o ludico e per svolgere compiti che richiedono un utilizzo intensivo della virgola mobile. Questi insiemi di istruzioni si trovano soprattutto nelle CPU più moderne.

Normalmente non è necessario aggiungere alcuna di queste opzioni al file a patto di utilizzare l'opzione   corretta (ad esempio   implica  ). Alcune eccezioni rilevanti a questa regola sono le CPU VIA e AMD64 più recenti, in quanto esse supportano insiemi di istruzioni, quali SSE3, che  non include. Se si possiedono tali CPU si consiglia di controllare l'output di  e di attivare le opzioni aggiuntive del caso.

Ma ottengo migliori prestazioni con -funroll-loops -fomg-optimize!
Non è vero. Si pensa questo solo perché qualcuno ci ha convinto che più opzioni si utilizzano meglio è. Le opzioni aggressive danneggiano le applicazioni se usate globalmente per tutto il sistema. Anche il manuale di  dice che usare   e   rendono il codice più grosso e più lento. Nonostante ciò queste due opzioni, assieme a,  ,   e simili, continuano ad essere molto popolari tra coloro che si vantano delle presunte prestazioni del proprio sistema.

La verità è che si tratta di opzioni aggressive e pericolose. Si invita il lettore a controllare il [forum] e il [bugzilla] di Gentoo per vedere quali sono le conseguenze di queste opzioni. Niente di buono!

Non bisogna aggiungere queste opzioni alle CFLAGS o alle CXXFLAGS. Esse possono indurre il lettore a credere di avere un sistema ad alte prestazioni e all'avanguardia, ma la realtà è che esse danneggiano il codice e costringono gli sviluppatori a chiudere i bug come INVALID o WONTFIX.

Non c'è bisogno di utilizzare tali opzioni. Non usarle. Limitarsi alle opzioni di base:,   e.

Livelli per -O maggiori di 3
Alcuni utenti si vantano di ottenere prestazioni maggiori usando,   e simili. In realtà i livelli per  maggiori di 3 non hanno alcun effetto. Il compilatore accetta CFLAGS come, ma in questi casi esso si limita ad applicare le ottimizzazioni del livello   e niente di più.

Per una dimostrazione si esamini il codice sorgente:

codice sorgente per -O

Il lettore può osservare che i valori maggiori di 3 sono trattati allo stesso modo che per.

Opzioni ridondanti
Spesso le CFLAGS e le CXXFLAGS che sono attivate ai vari livelli di  vengono specificate in modo ridondante nel file. Alcune volte lo si fa per ignoranza, ma in altri casi lo scopo è quello di aggirare la sostituzione o il filtraggio delle opzioni.

Il filtraggio e la sostituzione delle opzioni vengono svolti da molti ebuild. Solitamente lo si fa perché alcuni pacchetti non possono essere compilati a determinati livelli di, oppure perché il codice sorgente è troppo sensibile per permettere l'uso di opzioni aggiuntive. Tali ebuild possono filtrare alcune CFLAGS/CXXFLAGS o possono sostituire  con un livello differente.

Il Gentoo Developer Manual spiega nel dettaglio in che modo funziona il filtraggio e la sostituzione delle opzioni e dove essi hanno luogo.

È possibile aggirare il filtraggio delle opzioni attivate ad un certo livello di, come ad esempio  , nel seguente modo:

Specificare CFLAGS ridondanti

Questa, tuttavia, non è affatto una buona idea. Le CFLAGS sono filtrate per una ragione ben precisa! Tali opzioni vengono filtrate perché altrimenti non si è sicuri di riuscire a compilare un pacchetto correttamente. Ovviamente non è sicuro compilare l'intero sistema con  se alcune delle opzioni attivate da tale livello causano problemi con certi pacchetti. Il lettore non dovrebbe cercare di "raggirare" gli sviluppatori che mantengono questi pacchetti. Occorre fidarsi degli sviluppatori. Il filtraggio e la sostituzione delle opzioni viene fatto ad esclusivo vantaggio dell'utente finale. Se un ebuild specifica opzioni alternative non bisogna pertanto provare ad aggirarle.

Se si sceglie di compilare un pacchetto con opzioni inaccettabili è molto probabile che si avranno ulteriori problemi successivamente. Quando l'utente segnala un problema sul Bugzilla le opzioni che si usano in sono chiaramente visibili a tutti e gli sviluppatori chiederanno senz'altro di ricompilare il pacchetto senza le opzioni problematiche. Si può evitare il fastidio di dover ricompilare tali pacchetti se si evita in primo luogo di utilizzare opzioni ridondanti in questa maniera. Non si deve assumere di saperne di più rispetto agli sviluppatori.

LDFLAGS
Gli sviluppatori Gentoo hanno già scelto un insieme minimo e sicuro di LDFLAGS nei profili di base, pertanto non è necessario che l'utente vada a modificarle.

Opzioni specifiche per un singolo pacchetto
Le informazioni su come sia possibile applicare variabili d'ambiente (inclusa CFLAGS) ai singoli pacchetti sono descritte nel manuale Gentoo ("Variabili d'ambiente per pacchetto").

Risorse
Le seguenti risorse sono utili per approfondire ulteriormente il tema dell'ottimizzazione:


 * La documentazione per GCC


 * Capitolo 5 del Manuale Gentoo




 * Wikipedia


 * I forum Gentoo

Ringraziamenti
Si ringraziano le seguenti persone per i contributi apportati a questa guida:


 * nightmorph