Newsgroup: it.comp.programmare
Titolo: Su quali ottimizzazioni posso contare da parte del compilatore?

Ciao NG
 
E' ora di iniziare a sistemare delle lacune antiche, che
risalgono a oltre 20 anni fa. All'epoca presi abitudini
già allora cattive, per esempio dichiaravo variabili con
nomi "dummy_1" eccetera, riusandole più volte e per questo
denominandole con nomi non evocativi. Credevo, forse non
del tutto a torto, che fosse necessario parecchio
controllo da parte mia per sfruttare al meglio l'8088 e i
miseri 640 k. Ma molto è cambiato mentre le mie abitudini
sono cambiate molto meno.
 
Supponiamo di avere qualcosa del genere, in C.
 
int main ()
{
    int     a, b ;
 
    // uso solo "a"
    ..........
 
    // uso solo "b"
    ..........
}
 
Teoricamente il compilatore avrebbe modo di capire che non
le sto usando contemporaneamente e potrebbe generare il
codice usando una sola variabile.
 
Ma lo fa davvero? Questo è solo uno dei tanti esempi dei
dubbi che mi arrovellano. Conoscete per caso qualche link
che tratti l'argomento e da cui possa capire cosa fa il
compilatore, e io scrivere di conseguenza?
 
Uso il gcc/MinGW ma credo che il problema sia abbastanza
generale per rendere opportuno postare qua.
 
ciao!
 
Apx.
 


Buona domanda.
 
Come primissima cosa, che credo sia ovvia, dipende dagli switch del compilatore: a ottimizzatore staccato solitamente la traduzione cerca di essere il più fedele possibile, al massimo livello invece prende decisione a volte drastiche tagliando via o aggiungendo quello che ritiene opportuno: mi è capitato più volte di trovare codice che non funzionavano compilati al massimo livello di ottimizzazione; in ogni caso a questo livello il compilatore cerca di capire cosa volevi fare e lo scrive lui come meglio crede.
 
Secondo, a volte le ottimizzazioni di un compilatore decente sono molto superiori a quelle fattibili a livello di linguaggio: può essere ad esempio che, nello spezzone di codice che hai postato, il compilatore non usi neppure una variabile ma utilizzi solo dei registri (del processore, o di coprocessore/i, quando c'è/ci sono). Per quello che ne sappiamo, potrebbe anche mettere tutta la funzione inline o direttamente non compilarla se scopre che non viene mai chiamata :-)
 
Il compilatore, oltretutto, riesce ad ottimizzare meglio dei "pattern" che riconosce: magari uno spezzone di codice generico una volta compilato è più ottimizzato di uno su cui il programmatore ha cercato di fare qualche tipo di ottimizzazione.
 
In generale, non credo esista una risposta su *cosa* fa l'ottimizzatore, a parte andare a vedere cosa c'è nel binario.
 
Fondamentalmente però, come si dice spesso su (i.)c.l.c, questa è una discussione sterile per due motivi:
- Il compilatore sa fare l'ottimizzazione molto meglio di quanto potrebbe mai farlo un programmatore, quindi qualsiasi sforzo in questa direzione è inutile.
- Il "collo di bottiglia" non è mai su un problema di micro-ottimizzazione: è sempre relativo ad una scelta sbagliata di algoritmi di più alto livello.
 
Oltretutto, come diceva Ritchie a proposito delle ottimizzazioni, fare nel codice il lavoro del compilatore elimina alla base la necessità di usarne uno! ;)
 
Ciao!
 
P.S. Non ricordo chi me lo disse, ma era qualcosa del tipo: "nel mondo di programmatori bravi non ne esistono quasi più, e quei pochi che ci sono scrivono compilatori". Non credo sia vero, ma di sicuro chi sa scrivere un ottimizzatore la sa molto lunga (sicuramente molto più di me, e per questo mi fido del suo lavoro).

 Buona domanda.
 
... e grazie della buona risposta! Ora che ci penso, è
vero, potrebbe benissimo espandere tutto inline e usare
registri... brrrrrr... mi sento spodestato dal mio ruolo :D
 


Puoi sempre scrivere un compilatore! ;)
Ciao!
 
P.S. scherzi e opinioni generali a parte, il punto in cui mi sono trovato a leggere un listato assembly del compilato (ottimizzato) di uno programma e non riuscire a capire *nulla* di quello che stava succedendo, è stato quello che mi ha fatto convincere definitivamente delle idee che ho scritto - ed è successo svariati anni fa.
Su un x86-64 di oggi, poi, con la quantità di registri e operazioni presenti penso che non sarei in grado di leggere neppure un algoritmo scemo in virgola mobile (magari forse sì, ma dovrei andare a rileggere un sacco di roba che per ora sta bene dove sta, nel dimenticatoio del mio cervello) - per non parlare dei compilati ARM che mamma mia mi viene il magone (ma quanti cacchio di opcode sono riusciti a fare? Son pazzi! 32bit e son riusciti a usarli *tutti*! :D ).
Quindi sì, l'ottimizzazione è un argomento è super-interessante, ma IMHO è un lavoro a tempo pieno.. se tu hai altri target ti consiglierei (a malincuore) di lasciar stare - ma si può sempre cambiare lavoro! :)

 per non parlare dei compilati ARM che mamma mia mi viene il magone (ma quanti cacchio di opcode sono riusciti a fare? Son pazzi! 32bit e son riusciti a usarli *tutti*! :D ).
 
La cosa orribile dell'assembler ARM � che lo stack pointer non esiste, o 
meglio � un registro come gli altri. Seguire le chiamate di subroutine 
su asm ARM � un incubo, se non stai attento non capisci pi� niente dopo 
le prime due call :-(
 

 le prime due call :-(
 
Beh, alla fine lo FP (o R11) non è tanto diverso dallo SP degli x86. A volte anzi trovo più facile seguire le chiamate a funzione sugli ARM perché quando è possibile i parametri sono passati sui R0-3 e non sullo stack, che può rimanere costante a lungo. Se parli di programmazione kernel, penso tu dica che "è un registro come gli altri" perché non viene salvato in automatico sullo switch della modalità di CPU (a parte sulle FIQs dove è banked), ma in effetti non serve visto che puoi fare push dello stato sullo stack precedente e che in ogni caso ti devi ritirare su tutto un frame in caso di context-switch o, in caso di trap, switch su SVC (per il salvataggio di SPSP/LR per permettere ISR interrompibili) prima di riaccendere INT/IRQ/FIQ. Sugli M3 mi sembra poi che per lo FP avvenga in automatico, ma non ho mai scritto a questo livello per quella serie di CPU.
Quello che trovo più "fastidioso" in generale è come ho scritto sopra che c'è spesso un listatone di roba tipo "RSCLES r0,r5,r0,LSL r4" (che, per chi non ha dimestichezza con l'assembly, dice reverse subtract di r5 e r0, se è settato il flag di minore, mettendo il risultato in r0, aggiornando i flags e shiftando il risultato a sinistra di quando dice r4 - ovviamente con carry), che sinceramente mi toglie la voglia di cercare di capire cosa il compilatore sta cercando di fare (altrimenti, almeno io, devo prendere carta e penna e farsi tutti i passaggi a mano!).
Ciao!

 cambiare lavoro! :)
 
Noooooo! A parte che ne ho fin troppo a che fare con i PC
e la programmazione non è la mia tazza di te, è un tema
totalmente al di là della mia comprensione. Finché
esisterà qualcuno che lo fa, bene così :D
 
ciao!
 
Apx.