Ottimizzazione di GCC

From Gentoo Wiki
Jump to:navigation Jump to:search
This page is a translated version of the page GCC optimization and the translation is 30% complete.
Outdated translations are marked like this.

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.

Default CFLAGS can be set in make.conf for Gentoo systems. CFLAGS can also be specified per-package.

See also
For more information see CFLAGS and CXXFLAGS in the Gentoo Handbook, and the safe CFLAGS article. See also the FAQ.

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 qualsiasi build (compilazione) scritta correttamente dovrebbe venire a conoscenza di queste variabili per fornire opzioni extra o personalizzazioni quando si invoca il compilatore. Vedere la pagina delle informazioni GNU make per una lista di alcune delle variabili più usate in questa 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 sul modo in cui verrà costruito il sistema.

Possono essere usate per ridurre la quantità di messaggi di 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 dei loro scopi.

Come vengono usate?

Normalmente, CFLAGS e CXXFLAGS verrebbero inizializzate nell'ambiente al momento dell'invocazione di uno script configure o con Makefile generati da automake. Nei sistemi basati su Gentoo, le variabili CFLAGS e CXXFLAGS vengono impostate nel file /etc/portage/make.conf. Le variabili impostate in questo file verranno esportate nell'ambiente dei programmi invocati da portage, in modo tale che tutti i pacchetti vengano compilati usando quelle opzioni di base.

CODE Impostazioni CFLAGS in /etc/portage/make.conf
CFLAGS="-march=athlon64 -O2 -pipe" CXXFLAGS="${CFLAGS}"
Importante
Nonostante sia consentito scrivere le USE flag su più righe, fare altrettanto con CFLAGS può comportare problemi e sarà problematico per quei programmi come cmake. Per evitare problemi occorre assicurarsi che la dichiarazione delle CFLAGS stia su un'unica riga, con il minor numero di spazi possibile. Vedere ad esempio il bug #500034.

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

Suggerimento
L'articolo CFLAGS sicure potrebbe essere di aiuto ai principianti per iniziare ad ottimizzare i loro sistemi.

Fraintendimenti

Anche se le variabili CFLAGS e CXXFLAGS possono essere un modo valido per produrre codice binario più piccolo o più veloce, se utilizzate in modo errato, possono 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 riduzione delle prestazioni viene raggiunta piuttosto rapidamente quando si armeggia con le CFLAGS. Non si impostino arbitrariamente.

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

Pronto?

Ora che si è stati avvisati dei possibili rischi, diamo uno sguardo ad alcune equilibrate e sicure ottimizzazioni. Queste vi manterranno entro condizioni ottimali e saranno gradite agli sviluppatori quando ci sarà occasione di segnalare un problema su Bugzilla. (Gli sviluppatori di solito richiedono all'utente di ricompilare il pacchetto con un insieme ridotto di CFLAGS per vedere se il problema persiste. Si ricordi che opzioni aggressive possono rovinare il codice!)

Ottimizzazione

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 GCC (ce ne sono centinaia) ma verranno spiegate solo le opzioni fondamentali e quelle più comuni.

Nota
Qualora non si fosse sicuri del significato di una certa opzione si consulti il capitolo giusto del manuale GCC. Se si fosse ancora incerti, provare ad effettuare una ricerca su Internet oppure controllare le mailing list di GCC.

-march

La prima e più importante opzione è -march. 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 -march indica al compilatore di produrre codice specifico per la CPU del sistema, con tutte le sue funzionalità, caratteristiche, insiemi di istruzioni, stranezze, e così via, purché il codice sorgente sia pronto per utilizzarle. Per esempio, per trarre beneficio dalle istruzioni AVX, il codice sorgente deve essere adattato per supportarle.

-march= è un'opzione di selezione ISA; indica al compilatore che potrebbe usare le istruzioni dall'ISA. Su una piattaforma Intel/AMD64 con -march=native -O2 o un livello OPT inferiore, il codice probabilmente verrà concluso con le istruzioni AVX presenti ma servendosi di registri SSE XMM più brevi. Per sfruttare al meglio i registri AVX YMM, si dovrebbero usare anche le opzioni -ftree-vectorize, -O3 o -Ofast[1].

-ftree-vectorize è un'opzione di ottimizzazione (predefinita con -O3 e -Ofast), che tenta di vettorializzare i cicli usando l'ISA selezionata se possibile. La ragione per cui non viene abilitata con -O2 è che non sempre migliora il codice, può renderlo anche più lento, e solitamente più grande; ciò dipende da vari fattori, come anche dal tipo di ciclo, ecc.

Anche se la variabile CHOST nel file /etc/portage/make.conf specifica l'architettura generale utilizzata, -march dovrebbe comunque essere utilizzata così che i programmi possano essere ottimizzati per il processore specifico sul sistema. In particolare, le CPU x86 e x86-64 (fra le altre) dovrebbero utilizzare l'opzione -march.

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

user $cat /proc/cpuinfo

oppure installare app-portage/cpuid2cpuflags e aggiungere le opzioni specifiche della CPU al file /etc/portage/package.use/00cpuflags, cosa che viene fatta dallo strumento per esempio tramite la variabile CPU_FLAGS_X86:

user $cpuid2cpuflags
CPU_FLAGS_X86: aes avx avx2 f16c fma3 mmx mmxext popcnt sse sse2 sse3 sse4_1 sse4_2 ssse3
root #echo "*/* $(cpuid2cpuflags)" >> /etc/portage/package.use/00cpuflags

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

  • Il primo comando dice al compilatore di non effettuare alcun collegamento (-c), ed invece di interpretare l'opzione --help per fornire chiarimenti sulle opzioni da linea di comando, mostra quali opzioni sono abilitate o disabilitate (-Q). In questo caso, le opzioni mostrate sono quelle abilitate per l'obiettivo (target) selezionato:
user $gcc -c -Q -march=native --help=target
  • Il secondo comando mostrerà le direttive del compilatore per la costruzione del file header, ma senza effettuare le operazioni piuttosto mostrandole solamente sullo schermo (-###). La linea dell'output finale è il comando che contiene tutte le opzioni di ottimizzazione più l'architettura selezionata:
user $gcc -### -march=native /usr/include/stdlib.h
Nota
The l2-cache-size option represents processor's last level cache (L2 or higher if present).[2]
  • The glibc-hwcaps feature (>=sys-libs/glibc-2.33) can be used to define -march for a more general processor architecture (for >=sys-devel/gcc-11):
user $/lib64/ld-linux-x86-64.so.2 --help
...
Subdirectories of glibc-hwcaps directories, in priority order:
 x86-64-v4
 x86-64-v3 (supported, searched)
 x86-64-v2 (supported, searched)
 x86_64 (supported, searched)

In this example, the cpu supports x86-64-v3 psABI x86_64 which can be used for -march=x86-64-v3.

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

FILE /etc/portage/make.confEsempio Pentium III
CFLAGS="-march=pentium3"
CXXFLAGS="${CFLAGS}"

Invece questo esempio è per una CPU AMD a 64 bit:

FILE /etc/portage/make.confEsempio AMD64
CFLAGS="-march=athlon64"
CXXFLAGS="${CFLAGS}"

Se il tipo di CPU è indeterminato, o se l'utente non sa quale impostazione scegliere, è possibile usare l'opzione -march=native. Quando viene usata questa opzione, GCC proverà a rilevare il processore ed impostare automaticamente le opzioni più appropriate. Tuttavia, questa opzione non deve essere usata se l'intento è quello di compilare pacchetti per CPU diverse!

Se si compilano pacchetti su un computer per poi eseguirli su un altro computer (ad esempio nel caso in cui si dispone di un computer veloce che compila pacchetti da utilizzare su un computer più datato e lento), non bisogna utilizzare l'opzione -march=native. Native significa che il codice prodotto da una certa CPU potrà essere eseguito solo 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.

Esistono anche le opzioni -mtune e -mcpu. Esse sono solitamente utilizzate solo quando l'opzione -march non è disponibile; le architetture di alcuni processori possono richiedere -mtune o addirittura -mcpu. Sfortunatamente il comportamento di GCC non è molto coerente nel modo in cui ciascuna opzione si comporta passando da un'architettura all'altra.

Su CPU x86 e x86-64, -march genererà codice specifico per quella CPU usando il suo insieme di istruzioni disponibile ed il corretto ABI; non ci sarà retro compatibilità con CPU più vecchie o differenti. Si consideri l'uso di -mtune quando si genera codice per CPU più vecchie come le i386 e le i486. -mtune produce codice più generico rispetto a -march; sebbene possa accordare il codice per una certa CPU, non tiene conto dell'insieme di istruzioni disponibili e dell'ABI. Non utilizzare -mcpu su sistemi x86 o x86-64 in quanto è deprecato per quelle architetture.

Solo CPU diverse da x86/x86-64 (come SPARC, Alpha e PowerPC) possono richiedere -mtune o -mcpu invece di -march. Su queste architetture -mtune e -mcpu si comporteranno talvolta come -march (su architettura x86/x86-64) ma con un differente nome opzione. Nuovamente, il comportamento di GCC e la denominazione delle opzioni non è coerente attraverso le varie architetture, dunque per essere sicuri controllare il manuale di GCC per stabilire ciò che si dovrebbe usare.

Nota
Per ulteriori suggerimenti sulle impostazioni -march / -mtune / -mcpu si legga il capitolo 5 del Manuale di installazione di Gentoo relativo alla propria architettura. Si legga anche la lista dal manuale GCC delle opzioni specifiche per le architetture, ed anche le spiegazioni dettagliate riguardo le differenze tra -march, -mcpu</cpu>, e -mtune.
Attenzione
Non usare -march=native o -mtune=native nelle variabili CFLAGS o CXXFLAGS del file make.conf quando si compila con distcc.

-O

Attenzione
L'uso di -O3 o di -Ofast potrebbe causare la rottura di alcuni pacchetti durante la compilazione.
Nota
Per stampare tutti i pacchetti che sono stati compilati con CFLAGS/CXXFLAGS è possibile utilizzare il seguente comando: grep Ofast /var/db/pkg/*/*/CFLAGS

La prossima variabile da considerare è -O. Questa permette di controllare il livello complessivo di ottimizzazione. Cambiare questo valore comporterà un tempo di compilazione maggiore ed un utilizzo di memoria molto più grande, specialmente con l'innalzamento del livello di ottimizzazione.

Esistono sette possibili impostazioni per -O: -O0, -O1, -O2, -O3, -Os, -Og e -Ofast. Si scelga solo una tra esse nel file /etc/portage/make.conf.

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

Esaminiamo ora ciascuno dei livelli di ottimizzazione:

  • -O0: questo livello (che consiste nella lettera "O" seguita da uno zero) disattiva interamente l'ottimizzazione del codice ed è la scelta predefinita qualora nessuna opzione -O 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 nessuna ottimizzazione è attiva. Pertanto questa scelta è sconsigliata, a meno che lo scopo sia proprio il debug delle applicazioni.
  • -O1: è il livello di ottimizzazione base. Il compilatore proverà a produrre codice più veloce e più snello, senza richiedere troppo tempo per la compilazione. Si tratta di un'opzione base, ma dovrebbe fare un buon lavoro in ogni circostanza.
  • -O2: è il passo successivo rispetto a -O1. È il livello di ottimizzazione raccomandato, salvo esigenze particolari. -O2 attiva alcune ulteriori opzioni rispetto a quelle utilizzate da -O1. Con -O2 il compilatore proverà ad incrementare le prestazioni del codice senza comprometterne le dimensioni, e senza richiedere un tempo eccessivo. In questo livello SSE o AVX potrebbero essere utilizzate, ma nessun registro YMM verrà usato a meno che non sia abilitato anche -ftree-vectorize.
  • -O3: è il livello di ottimizzazione più alto possibile. Vengono attivate ottimizzazioni costose dal punto di vista del tempo di compilazione e dell'utilizzo di memoria. Compilare con -O3 non è un modo sicuro di migliorare le prestazioni del codice; in molti casi si ottiene il rallentamento del sistema a causa di codice binario più grande e dell'utilizzo maggiore di memoria. -O3 è anche noto per causare la corruzione di alcuni pacchetti. Usare -O3 non è raccomandato. Tuttavia, abilita anche -ftree-vectorize così che i cicli nel codice diventino vettorializzati e saranno usati i registri AVX YMM.
  • -Ofast: si tratta di un'opzione introdotta con GCC 4.7. Essa consiste della somma di -O3 con -ffast-math, -fno-protect-parens e -fstack-arrays. Questa opzione viola la rigorosa conformità agli standard e pertanto non è consigliata.
  • -Os: ottimizza il codice dal punto di vista delle dimensioni. Attiva tutte le opzioni di -O2 che non comportino l'incrementano delle dimensioni del codice prodotto. -Os può essere utile per macchine che hanno una capacità di archiviazione su disco estremamente limitata o CPU con una piccola cache.
  • -Oz: Introduced in GCC 12.1, more aggressively optimize for size than -Os. Note this will heavily degrade runtime performance than -O2, due to increasing the number of instructions executed if those instructions require fewer bytes to encode.
  • -Og: 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 -Og l'attività di sviluppo dovrebbe risultare migliore rispetto a -O0. Si noti che -Og non implica -g; piuttosto si limita semplicemente a disattivare le ottimizzazioni che hanno ripercussioni negative sull'attività di debug.

Come menzionato precedentemente -O2 è il livello di ottimizzazione raccomandato. Se la compilazione di un pacchetto fallisce e non si stava usando -O2, 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 -O1 o addirittura -O0 -g2 -ggdb (per la segnalazione degli errori ed il controllo di possibili problemi).

-pipe

Un'opzione piuttosto comune è -pipe. Essa non ha effetti sul codice generato ma rende più rapido il processo di compilazione. Comunica al compilatore di utilizzare le pipe (trasferimento dati) al posto di file temporanei durante le varie fasi della compilazione, la qual cosa utilizzerà più memoria. Tale opzione potrebbe causare problemi su sistemi con poca memoria, perché GCC potrebbe essere terminato. In quest'ultimo caso si sconsiglia di utilizzare tale 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 di -O (eccetto -O0) su quelle architetture dove ciò non interferisce con il debug (per esempio x86-64), ma potrebbe essere necessario attivare. Benché il manuale di GCC non specifichi tutte le architetture, ciò si attiva usando l'opzione -O. È necessario esplicitare -fomit-frame-pointer per attivarlo su x86-32 con GCC fino alla versione 4.6, o quando si usa -Os su x86-32 con qualunque versione di GCC. Tuttavia, utilizzando -fomit-frame-pointer, il debug del codice diventa molto difficile o addirittura impossibile.

In particolare, rende la risoluzione dei problemi molto più difficile per le applicazioni scritte in Java e compilate da gcj, sebbene questo non sia l'unico linguaggio influenzato. Dunque, pur potendo l'opzione essere utile, rende il debug più arduo; in particolare il backtrace (tracciamento dei passi precedenti) risulterà inutilizzabile. Se non si effettua il debug dei programmi e non vengono usate altre variabili CFLAGS relative al debug, come per esempio -ggdb, allora si può tentare di usare -fomit-frame-pointer.

Importante
Non combinare -fomit-frame-pointer con l'opzione -momit-leaf-frame-pointer. Quest'ultima è sconsigliata in quanto la prima già da sola svolge correttamente il lavoro. Inoltre -momit-leaf-frame-pointer è nota per avere conseguenze negative sulle prestazioni del codice.

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

Queste opzioni attivano gli insiemi di istruzioni Streaming SIMD Extensions (SSE), SSE2, SSE3, MMX, e 3DNow! per le architetture x86 e x86-64. Tali opzioni sono principalmente utili per la multimedialità, i giochi ed altri intensi compiti di calcolo a virgola mobile. Questi insiemi di istruzioni si trovano nelle CPU più moderne.

Importante
Assicurarsi che la propria CPU supporti queste opzioni eseguendo il comando cat /proc/cpuinfo. L'output include ogni aggiuntivo insieme di istruzioni supportato. Si noti che pni è solo un nome differente per indicare SSE3.

Normalmente nessuna di queste opzioni è necessario aggiungere al file /etc/portage/make.conf, a patto che il sistema utilizzi l'opzione -march corretta (ad esempio -march=nocona implica -msse3). Alcune eccezioni rilevanti a questa regola sono le CPU VIA e AMD64 più recenti, le quali supportano insiemi di istruzioni non incluse con -march (per esempio SSE3). Per CPU come queste, sarà necessario abilitare opzioni aggiuntive appropriate dopo aver controllato /proc/cpuinfo.

Nota
Controllare l'elenco delle opzioni (flag) specifiche per x86 e x86-64 per vedere quali di questi insiemi di istruzioni vengono correttamente attivati dal tipo di opzione (flag) per la CPU. Se un insieme di istruzioni compare nella lista, non serve specificarlo separatamente; verrà abilitato attraverso la corretta impostazione di -march.

Hardening optimizations

Nota
While it is possible to use a hardened profile, it certainly isn't necessary for adding some hardening flags to /etc/portage/make.conf on a different profile. Especially on a desktop system, the use of position independent code (PIC) and position independent executables (PIE) on a system-wide level may cause compilation failures.

Hardening an otherwise unhardened system, like when using a desktop profile, can be considered a GCC optimization as well, especially in the light of security vulnerabilities such as Meltdown and Spectre.

Some packages feature an individual hardened USE flag, enabling tested security enhancements (like CFLAGS/CXXFLAGS). It may be a good idea to set this system-wide in /etc/portage/make.conf.

Nota
Reading section Do I need to pass any flags to LDFLAGS/CFLAGS in order to turn on hardened building? in the Hardened/FAQ is recommended for retrieving some basic hardened CFLAGS/CXXFLAGS.
Attenzione
Changing the CFLAGS/CXXFLAGS can cause problems and in some cases may even render a system unusable. Rebuilding the whole system with emerge -e @world may resolve the situation.

Overflow protection

Optimizing CFLAGS/CXXFLAGS for overflow protection can be a good idea if security concerns outweigh speed optimization. This may be the case on a daily-use desktop system, while e.g. on an optimized gaming PC it will be considered counterproductive.

For GCC version 12, package sys-devel/gcc, the USE flags default-stack-clash-protection and default-znow will automatically enable additional overflow protection.

Nota
A lot of these flags are now applied internally through the GCC toolchain automatically under the hardened profile, and some even under the regular profile. See table at Hardened/Toolchain.
CFLAGS/CXXFLAGS LDFLAGS function
-D_FORTIFY_SOURCE=2 run-time buffer overflow detection
-D_GLIBCXX_ASSERTIONS run-time bounds checking for C++ strings and containers
-fstack-protector-strong stack smashing protector
-fstack-clash-protection increased reliability of stack overflow detection
-fcf-protection control flow integrity protection
-Wl,-z,defs detect and reject underlinking
-Wl,-z,now disable lazy binding
-Wl,-z,relro read-only segments after relocation

ASLR

Address Space Layout Randomization (ASLR) is measure to increase security by randomly placing each function and buffer in memory. This makes it harder for attack vectors to succeed.

PIE is enabled by default when it is safe to do so on all 17.0 profiles[3]. PIC may also be enabled by default on executables for architectures that require it (like AMD64).

There is no need to set PIE or PIC manually in CFLAGS.

CFLAGS/CXXFLAGS LDFLAGS function
-fpie -Wl,-pie full ASLR for executables
-fpic -shared no text relocations for shared libraries

Domande frequenti

Higher version of GCC should mean better optimizations?

Not always because of software regression, where an optimization with an earlier version of GCC no longer optimizes. A full list of regressions can be found at this link. Should this happen, please file a bug to Gentoo's bugzilla and/or GCC's bugzilla.

Is there a perfect optimizer?

No, because it would solve the halting problem, where it can tell if any program stops or runs forever [4].

What about optimizing GCC itself?

gcc has pgo and lto use flags that enables Profile Guided Optimization and Link Time Optimization respectively. To enable for building gcc itself with PGO and LTO:

FILE /etc/portage/package.use/gcc
sys-devel/gcc pgo lto

In Gentoo, a 3-stage bootstrap of gcc is done, meaning it compiles itself three times [5]. In stage1, gcc is complied using an older gcc. In stage2, gcc is compiled using stage1 gcc. In stage3, gcc is compiled using stage2 gcc and is used to verify that stage2 gcc and stage3 gcc are the same. This is done because it is tested more completely and has better performance. The lto use flag adds -flto to BOOT_CFLAGS. The pgo use flag adds -fprofile-generate to stage2 gcc and adds -fprofile-use -fprofile-reproducible=parallel-runs to stage4 gcc.

gcc performance may improve via PGO, although it may as much as double the compile times.

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 GCC dice che usare -funroll-loops e -funroll-all-loops rendono il codice più grosso e più lento. Nonostante ciò queste due opzioni, assieme a -ffast-math, -fforce-mem, -fforce-addr 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.

Opzioni pericolose come queste non sono necessarie. Non si usino. Limitarsi alle opzioni di base: -march, -O e -pipe.

Riguardo ai livelli per -O maggiori di 3?

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

Per una dimostrazione si esamini il codice sorgente:

CODE -O source code
case OPT_LEVELS_3_PLUS:
      enabled = (level >= 3);
      break;
 
 case OPT_LEVELS_3_PLUS_AND_SIZE:
      enabled = (level >= 3 || size);
      break;

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

Riguardo la compilazione esterna alla macchina di destinazione?

Alcuni lettori potrebbero chiedersi se compilare fuori dalla macchina di destinazione con una CPU o una sotto architettura GCC seriamente inferiori portino a risultati di ottimizzazione inferiori (rispetto ad una compilazione nativa). La risposta è semplicemente: No. A prescindere dall'hardware che effettivamente esegue la compilazione ed il CHOST per il quale GCC è stato compilato, finché vengono usati gli stessi argomenti (fatta eccezione per -march=native) e la stessa versione di GCC (sebbene versioni minori potrebbero comportarsi diversamente), le ottimizzazioni risultanti sono esattamente le stesse.

Per esemplificare, se Gentoo è installato su una macchina il cui CHOST di GCC è i686-pc-linux-gnu e un server Distcc è impostato su un altro computer il cui CHOST di GCC è i486-linux-gnu, allora non c'è motivo di temere che i risultati siano meno ottimali a causa della sotto architettura categoricamente inferiore del compilatore in remoto e/o dell'hardware. Il risultato verrebbe ottimizzato tanto quanto una compilazione nativa, a condizione che le stesse opzioni siano passate a entrambi i compilatori (e il parametro -march non riceva l'argomento native). In un caso così particolare, l'architettura di destinazione deve essere specificata esplicitamente, come spiegato su Distcc and -march=native.

La sola differenza di comportamento tra due versioni GCC costruite avendo come bersaglio sotto architetture diverse consiste nell'argomento implicito predefinito per -march, che deriva dal CHOST del GCC quando non è esplicitamente fornito da linea di comando.

Riguardo le opzioni ridondanti?

Spesso le CFLAGS e le CXXFLAGS che vengono attivate a vari livelli di -O sono specificate in modo ridondante nel file /etc/portage/make.conf. Alcune volte lo si fa per ignoranza, ma in altri casi lo scopo è evitare il filtraggio o la sostituzione delle opzioni (flag).

Il filtraggio/sostituzione delle opzioni è usato da molti ebuild nell'albero di Portage. Solitamente viene fatto perché alcuni pacchetti falliscono la compilazione con certi livelli di -O, o quando il codice sorgente è troppo sensibile per l'uso di opzioni aggiuntive qualsiasi. Tali ebuild possono filtrare alcune CFLAGS e CXXFLAGS o possono sostituire -O con un livello differente.

Il Manuale dello Sviluppatore di Gentoo spiega nel dettaglio in che modo funziona il filtraggio e la sostituzione delle opzioni e dove essi hanno luogo.

È possibile raggirare il filtraggio di -O attraverso un elenco ridondante di opzioni per un certo livello, ad esempio -O3, come in questo modo:

CODE Specificare CFLAGS ridondanti
CFLAGS="-O3 -finline-functions -funswitch-loops"

Comunque, questa non è una buona idea. Le CFLAGS sono filtrate per una ragione precisa! Quando le opzioni vengono filtrate significa che non è sicuro compilare un pacchetto con quelle. Ovviamente non è sicuro compilare l'intero sistema con -O3 se alcune delle opzioni attivate da tale livello causano problemi con certi pacchetti. Pertanto, non si cerchi di "superare" gli sviluppatori che mantengono questi pacchetti. Occorre fidarsi degli sviluppatori. Il filtraggio e la sostituzione delle opzioni vengono fatti per assicurare la stabilità del sistema e delle applicazioni! Se una ebuild specifica opzioni (flag) alternative non bisogna provare a raggirarle.

Se si sceglie di compilare un pacchetto con opzioni inaccettabili è molto probabile che si andrà incontro a problemi. Quando l'utente segnala un problema su Bugzilla, le opzioni usate in /etc/portage/make.conf saranno chiaramente visibili 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 l'utilizzo di opzioni ridondanti in quella maniera! Non si deve presumere di saperne di più degli sviluppatori.

In merito alle LDFLAGS?

Gli sviluppatori Gentoo hanno già scelto un insieme minimo e sicuro di LDFLAGS nei profili di base, pertanto non è necessario modificarlo.

Posso usare opzioni specifiche per singolo pacchetto?

Attenzione
Usare opzioni specifiche per singolo pacchetto complica il debug e l'assistenza. In questi casi, assicurarsi di menzionare la funzionalità che si sta utilizzando e le modifiche apportate.

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

Profile Guided Optimization (PGO)

Profile guided optimization (PGO) consists of compiling and profiling a program to assess hot paths in the code. Optimizations are then applied based on this analysis. Specifically, the code is compiled with -fprofile-generate, which instrument the code. Second, the code is run with applications to collect profile information. Finally, using the profiled data, the code is compiled with -fprofile-use. To manually enable PGO for packages, see this link.

Firefox also supports PGO although sometimes it may break the build.

Link Time Optimization (LTO)

Nota
LTO heavily increases compile times and if changing even one object file when compiling, LTO recompiles the whole code again. There is a ongoing GSoC project to make sure LTO only recompiles what it deems necessary.

LTO is still experimental. LTO may need to be disabled before reporting bugs because it is a common source of problems. The -flto flag is used, with an optional auto argument (Detects how many jobs to use) or a integer argument (An integer number of jobs to execute parallel).

See the LTO article for more information on LTO on Gentoo.

Vedere anche

Risorse esterne

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

Riferimenti



This page is based on a document formerly found on our main website gentoo.org.
The following people contributed to the original document:
They are listed here because wiki history does not allow for any external attribution. If you edit the wiki article, please do not add yourself here; your contributions are recorded on each article's associated history page.