GCC optimization/it

Questa guida fornisce 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 due tra le variabili d'ambiente che vengono convenzionalmente utilizzate per specificare opzioni di compilazione ad un sistema in via di costruzione quando si compila codice C e C++. Benché queste variabili non siano standardizzate, il loro uso è essenzialmente ubiquitario e qualunque build scritta corettamente dovrebbe capire queste variabili per passare opzioni extra o personalizzate quando invoca il compilatore. Vedere la pgina di informazioni GNU make per una lista di alcune delle variabili più usate inq uesta categoria.

Poiché la maggior parte dei pacchetti che costituiscono i sistemi Gentoo sono scritti in C e C++, queste sono due variabili che gli amministratori dovranno impostare correttamente, in quanto eserciteranno una grande influenza su come il sistema verrà costruito.

Possono essere usate per ridurre la quantità di messaggi per il debug per un programma, 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?
Normalmente, CFLAGS e CXXFLAGS verrebbero inizializzate nell'ambiente al momento dell'invocazione di uno script configure o con Makefile generati da. Nei sistemi basati su Gentoo, le variabili CFLAGS e CXXFLAGS vengono impostate nel file. Le variabili impostate in questo file verranno esportate nell'ambiente dei programmi invocati da portage, in modo tale che tutti i pacchetti vengano compilati usano quelle opzioni come base.

Come è possibile vedere nell'esempio di cui sopra, la variabile CXXFLAGS è configurata in modo da usare le stesse opzioni presenti nelle CFLAGS. La maggior parte dei sistemi dovrebbe essere configurata in questa maniera. Le opzioni aggiuntive in CXXFLAGS sono meno comuni e non si dovrebbe configurarle globalmente.

Convinzioni errate
Anche se le variabili CFLAGS e CXXFLAGS possono essere un modo molto valido per produrre codice binario più piccolo o più veloce, se utilizzate in modo errato, possono anche compromettere la funzionalità del codice stesso, aumentare a dismisura le sue dimensioni, ridurre drasticamente le prestazioni. La loro configurazione errata può anche provocare errori di compilazione. La soglia di diminuzione delle prestazioni viene raggiunta piuttosto velocemente quando si ha a che fare con le CFLAGS. Non è consigliabile configurarle arbitrarialmente.

È bene ricordare che la variabile globale CFLAGS configurata in verrà applicata ad ogni pacchetto nel sistema, pertanto gli amministratori configureranno solo opzioni generali e applicabili su vasta scala. I pacchetti modificano ulteriormente queste opzioni individualmente nell'ebuild o nel sistema compilato stesso per generare l'insieme di flag finale usato quando si invoca il compilatore.

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 questa guida si limiterà ad utilizzare combinazioni note per funzionare bene. Idealmente, si tratta delle migliori combinazioni per ogni architettura di CPU. Verrà fatta menzione delle opzioni aggressive più in là, 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 prima e più importante opzione è. Questa comunica al compilatore di produrre codice per una certa architettura di processori (o "arch"); essenzialmente, dice a GCC di produrre codice per un certo tipo di CPU. CPU diverse possiedono diverse funzionalità, supportano diversi insiemi di istruzioni e hanno diverse modalità di esecuzione del codice. L'opzione  indica al compilatore di produrre codice specifico per una data CPU, considerando tutte le sue funzionalità, caratteristiche, insiemi di istruzioni, stranezze, e così via, a patto che il codice sorgente sia pronto per utilizzarle. Per esempio, per sfruttare al meglio le istruzioni AVX, il codice sorgente deve essere adattato per supportarle.

è un'opzione di selezione ISA; indica al compilatore che potrebbe usare le istruzioni dall'ISA. Su una piattaforma Intel/AMD64 con  o livello OPT inferiore, il codice finirà per utilizzare istruzioni AVX con registri SSE XMM più brevi. Per sfruttare al meglio i registri AVX YMM, si dovrebbero usare anche le opzioni,   o.

è un'opzione di ottimizzazione (predefinita a  e  ), che tenta di vettorializzare i cicli usando l'ISA selezionata se possibile. La ragione per cui non viene abilitata con  è che non sempre migliora il codice: infatti, può renderlo più lento, e solitamente più grande; dipende da vari fattori, come ad esempio dal tipo di ciclo, etc.

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 (fra le altre) che dovrebbero utilizzare l'opzione.

Quale CPU è presente nel sistema? Per scoprirlo, si esegua il seguente comando:

oppure installare e aggiungere le opzioni specifiche della CPU al file, cosa che viene fatta dallo strumento  per esempio tramite la variabile CPU_FLAGS_X86:

Per ottenere maggiori dettagli, compresi i valori di  e , si possono usare due comandi.


 * Il primo comando dice al compilatore di non effettuare alcun link, e invece di interpretare l'opzione  per dettagliare le opzioni della linea di comando, mostra se certe opzioni sono abilitate o disabilitate . In questo caso, le opzioni mostrate sono abilitate per il target selezionato:


 * Il secondo comando mostra le direttive del compilatore per la costruzione del file header, ma senza effettuare effettivamente le operazioni e mostrandole invece sullo schermo . L'output completo è il comando che contiene tutte le opzioni di ottimizzazione e la selezione dell'architettura:

Si veda ora  in azione. Questo è un esempio per un vecchio processore Pentium III:

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

Se il tipo di CPU è indeterminato, o se l'utente non sa quali impostazioni scegliere, è possibile usare l'opzione. Quando viene usata questa ozpione, GCC proverà a determinare automaticamente di quale processore si dispone, impostando di conseguenza le opzioni più appropriate. Questa opzione, tuttavia, non dovrebbe essere utilizzata qualora si intenda compilare pacchetti per CPU diverse!

Se si compilano pacchetti su un computer per poi eseguirli su un computer diverso (ad esempio nel caso in cui si dispone di un computer veloce che compila pacchetti da utilizzare su un vecchio 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. Si consideri l'utilizzo di  quando si genera codice per CPU più vecchie come le i386 e le 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 GCC non è consistente su tutte le architetture, e che pertanto occorre controllare il manuale per determinare quali opzioni si dovrebbero usare.

-O
La prossima variabile da considerare è. Questa variabile permette di controllare il livello complessivo di ottimizzazione. Cambiare questo valore risulterà in un tempo di compilazione maggiore e in un utilizzo maggiore di memoria, specialmente se si innalza il livello di ottimizzazione.

Esistono sette possibili impostazioni per : ,  ,  ,  ,  ,   e. Sceglierne solamente una tra esse 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 ora ciascuno dei livelli di ottimizzazione:


 * : Questo livello (che consiste nella lettera "O" seguita da uno zero) disattiva interamente l'ottimizzazione del codice ed è la scelta predefinita qualora nessuna opzione  venga specificata in CFLAGS o CXXFLAGS . Ciò 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 basilare, ma che 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. In questo livello potrebbero essere utilizzate SSE o AVX ma nessun registro YMM verrà usato a meno che non venga abilitato anche.


 * : 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. Usare   non è raccomandato. Tuttavia, abilita anche   in modo tale che i cicli nel codice diventino vettorializzati e userà i registri AVX YMM.


 * : ottimizza il codice dal punto di vista delle dimensioni. 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 la compilazione di un pacchetto fallisce 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   (per la segnalazione di errori e il controllo di possibili problemi).

-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. Benché il manuale di GCC non specifichi tutte le architetture, è attivato utilizzando l'opzione. È necessario esplicitare, per attivarlo su x86-32 con GCC fino alla versione 4.6, o quando si usa   su x86-32 con qualunque versione di GCC. Tuttavia, utilizzando, 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. Se non si effettua il debug di programmi e non vengono usate altre variabili CFLAGS relative al debug, come per esempio, allora si può tentare di usare.

-msse, -msse2, -msse3, -mmmx, -m3dnow
These flags enable the Streaming SIMD Extensions (SSE), SSE2, SSE3, MMX, and [https://en.wikipedia.org/wiki/3DNow! 3DNow!] instruction sets for x86 and x86-64 architectures. These are useful primarily in multimedia, gaming, and other floating point-intensive computing tasks, though they also contain several other mathematical enhancements. These instruction sets are found in more modern CPUs.

Normally none of these flags need to be added to, as long as the system is using the correct  (for example,   implies  ). Some notable exceptions are newer VIA and AMD64 CPUs that support instructions not implied by  (such as SSE3). For CPUs like these additional flags will need to be enabled where appropriate after checking.

Ma ottengo migliori prestazioni con -funroll-loops -fomg-optimize!
No, people only think they do because someone has convinced them that more flags are better. Aggressive flags will only hurt applications when used system-wide. Even the GCC manual says that using  and   will make code larger and run more slowly. Yet for some reason, these two flags, along with,  ,  , and similar flags, continue to be very popular among ricers who want the biggest bragging rights.

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!

These flags are not needed globally in CFLAGS or CXXFLAGS. They will only hurt performance. They might bring on the idea of having a high-performance system running on the bleeding edge, but they don't do anything but bloat the code and get bugs marked INVALID or WONTFIX.

Dangerous flags like these are not needed. Don't use them. Stick to the basics:,  , and.

Livelli per -O maggiori di 3
Some users boast about even better performance obtained by using,  , and so on, but the reality is that   levels higher than 3 have no effect. The compiler may accept CFLAGS like, but it actually doesn't do anything with them. It only performs the optimizations for, nothing more.

Need more proof? Examine the source code:

As can be seen, any value higher than 3 is treated as just.

Che dire della compilazione all'esterno della macchina bersaglio?
Alcuni lettori potrebbero chiedersi se compilare fuori dalla macchina bersaglio con una CPU od un'architettura GCC strettamente inferiori possa portare a risultati di ottimizzazione inferiori (in confronto ad una compilazione nativa). La risposta è semplicemente: No. A prescindere dall'hardware effettivo su cui avviene la compilazione e dal CHOST per cui GCC è stato costruito, finché vengono usati gli stessi argomenti (eccezion fatta per ) e la stessa versione di GCC (benché la versione minore possa essere diversa), le ottimizzazioni risultatni sono esattamente le stesse.

Per riportare un esempio, se Gentoo viene installato su una macchina il cui CHOST GCC è i686-pc-linux-gnu, e viene installato un server Distcc su un altro computer il cui CHOST GCC è i486-linux-gnu, allora non c'è bisogno di aver timore di ottenere risultati meno ottimali, a causa della sub-architettura strettamente inferiore del compilatore e/o dell'hardware remoti. Il risultato sarebbe ottimizzato al pari di una compilazione nativa, a patto che vengano passate le stesse opzioni ad ambo i compilatori (e che il parametro  non riceva un argomento  ). In questo caso particolare, è necessario specificare esplicitamente l'architettura bersaglio, come spiegato in Distcc e -march=native.

La sola differenza nel comportamento tra le due versioni di GCC costruite avendo come bersaglio diverse sub-architetture consiste nell'argometno implicito predefinito per il parametro, che è derivato dal CHOST di GCC quando non è esplicitamente fornito da linea di comando.

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 e 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:

However, this is not a smart thing to do. CFLAGS are filtered for a reason! When flags are filtered, it means that it is unsafe to build a package with those flags. Clearly, it is not safe to compile the whole system with  if some of the flags turned on by that level will cause problems with certain packages. Therefore, don't try to "outsmart" the developers who maintain those packages. Trust the developers. Flag filtering and replacing is done to ensure stability of the system and application! If an ebuild specifies alternative flags, then don't try to get around it.

Building packages with unacceptable flags will most likely lead to problems. When reporting problems on Bugzilla, the flags that are used in will be readily visible and developers will ask to recompile without those flags. Save the trouble of recompiling by not using redundant flags in the first place! Don't just automatically assume to be more knowledgeable than the developers.

LDFLAGS
The Gentoo developers have already set basic, safe LDFLAGS in the base profiles, so they do not need to be changed.

Opzioni specifiche per un singolo pacchetto
Information on how to use per-package environment variables (including CFLAGS ) is described in the Gentoo Handbook, "Per-Package Environment Variables".

Vedere anche

 * Configurazione delle opzioni di compilazione (Manuale AMD64)

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


 * La documentazione online per GCC




 * Wikipedia


 * I forum Gentoo