Una modifica minuscola dentro GCC può pesare più di quanto suggerisca il diff. Il caso del tuning generico x86 lo mostra bene: una sola riga nel modello dei costi del compilatore aumenta il peso assegnato a una branch misprediction e, almeno in un test di SPEC CPU 2017, porta un incremento superiore al 12% su Intel Granite Rapids e AMD Zen 5. Non è una magia del compilatore; è il risultato di un aggiustamento che riallinea una vecchia euristica alla realtà delle CPU moderne.
GCC ha una storia lunga: nasce come compilatore C del progetto GNU e nel tempo diventa una raccolta di frontend e backend per molti linguaggi e molte architetture. Sulla piattaforma x86, in particolare, il compilatore convive da decenni con processori molto diversi tra loro: dal codice pensato per compatibilità ampia fino all’utilizzo con “target” specifici come i chip Intel Xeon Granite Rapids o gli AMD Zen 5.
Il punto centrale, tuttavia, è che una grande quantità di software non nasce con ottimizzazioni per una singola CPU. Molti pacchetti Linux, librerie distribuite in forma binaria, container e build generiche usano ancora un profilo x86-64 pensato per funzionare bene sul parco più ampio delle possibili configurazioni hardware, non per spremere una singola microarchitettura.
Perché una previsione di salto sbagliata costa tanto
Le CPU x86 eseguono molte istruzioni in modo speculativo: quando incontrano un salto, provano a indovinare il percorso più probabile e riempiono in anticipo la catena di esecuzione con istruzioni prese da quel ramo (il meccanismo, ben noto, si chiama branch prediction). Se la previsione si rivela corretta, il programma procede senza pagare un costo evidente. Se invece il percorso non è quello corretto, il processore deve scartare lavoro già iniziato, ripulire lo stato e ripartire dal percorso corretto.
Su processori ad alte prestazioni, il costo di una previsione errata non coincide con una semplice istruzione persa. Può significare cicli di clock buttati via, meno parallelismo utile, ritardi nel caricamento dei dati e minore efficienza delle unità di esecuzione.
Come una singola riga cambiata nel compilatore può migliorare le prestazioni
Lili Cui, ingegnere software Intel, ha scoperto che vale la pena aumentare il costo di errata predizione dell’ottimizzazione generica x86 per il compilatore GCC.
Dato che le CPU moderne hanno pipeline più profonde e quindi rendono le errate predizioni dei salti più costose, aumentare il costo nella tabella di ottimizzazione generica aiuta a evitare blocchi della pipeline dovuti a salti errati.
Semplicemente aumentando di un valore 3 la penalizzazione applicata a valle di una errata predizione dei salti, si è riscontrato un aumento delle prestazioni su Granite Rapids del 12,7% e su AMD Zen 5 del 12,1%.
È un risultato molto interessante, soprattutto considerando che si tratta di una singola riga di codice modificata, peraltro con un intervento banale.
Un compilatore come GCC non si limita a ottimizzare il codice sorgente: ottimizza anche le proprie stime interne. Se la valutazione delle previsioni di salto errate è troppo bassa, anche le ottimizzazioni successive possono orientarsi verso soluzioni meno efficaci. Una singola riga modificata non riscrive GCC, ma corregge lo strumento che guida le sue decisioni. La modifica proposta da Lili Cui sarà integrata in GCC 17.