Vediamo come impattano le opzioni di ottimizzazione del compilatore (GCC), sul tempo di esecuzione del codice.
Il caso di studio considera la risoluzione numerica di un integrale definito, una semigaussiana, con un totale di 108 iterazioni. Vediamo il codice di riferimento, già ben ottimizzato.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
int main(){
register unsigned int i;
register unsigned int N=100000000;
register float ris=0;
register float a=0;
register float b=20;
register float h=1.0*(b-a)/N;
register float pi=3.14159265;
clock_t start = clock();
for(i=0;i<N;i++){
ris+=exp(-0.5*(a+i*h)*(a+i*h));
}
ris*=1.0*h/sqrt(2*pi);
clock_t end = clock();
printf("ris=%f\n",ris);
printf("%f\n",(float)(end-start)/CLOCKS_PER_SEC);
return 0;
}
Ora vediamo come cambiano impattano le opzioni di ottimizzazione del compilatore:
- nessuna ottimizzazione, predefinito:
gcc nomefile.c -o nomefile
: tempo medio di esecuzione 0,575 secondi
- ottimizzazioni di base:
gcc -O1 nomefile.c -o nomefile
: tempo medio di esecuzione 0,493 secondi
- ottimizzazioni moderate:
gcc -O2 nomefile.c -o nomefile
: tempo medio di esecuzione 0,485 secondi
- ottimizzazioni aggressive:
gcc -O3 nomefile.c -o nomefile
: tempo medio di esecuzione 0,485 secondi
- ottimizzazioni estreme e rischiose:
gcc -Ofast nomefile.c -o nomefile
: tempo medio di esecuzione 0,457 secondi, attenzione poiché <<questo livello attiva ottimizzazioni aggressive che possono non rispettare pienamente gli standard IEEE o altre specifiche del linguaggio, ma possono migliorare ulteriormente le prestazioni>>
- ottimizzazioni con specifiche del processore:
gcc -O2 -march=native nomefile.c -o nomefile
: tempo medio di esecuzione 0,479 secondi (rispetto a 0,485 del caso "ottimizzazioni moderate")
Nota: le ottimizzazioni del compilatore sono quindi importanti e mostrano un effettivo guadagno di performance rispetto alla compilazione standard, senza ottimizzazioni. Valutare attentamente se convengano ottimizzazioni aggressive o addirittura estreme, che possono compromettere sia sicurezza che funzionalità (e non è nemmeno detto poi che il guadagno in termini di tempo di esecuzione sia così grande, dipende anche dal tipo di problema computazionale da risolvere). Il miglior compromesso probabilmente è quello delle ottimizzazioni moderate, meglio ancora se con le specifiche del processore, nel caso il codice fosse finalizzato a girare su quella macchina, o macchine simili.