Ottimizzare C++/Scrivere codice C++ efficiente/Costrutti che migliorano le prestazioni: differenze tra le versioni
Contenuto cancellato Contenuto aggiunto
Nessun oggetto della modifica Etichetta: Editor wikitesto 2017 |
|||
Riga 31:
Per esempio, se hai il seguente array di strutture:
<
struct S {
int a, b;
};
S arr[n_voci];
</syntaxhighlight>
e vuoi ordinarlo in base al campo <code>b</code>, potresti definire la seguente funzione di confronto:
<
bool confronta(const S& s1, const S& s2) {
return s1.b < s2.b;
}
</syntaxhighlight>
e passarla all'algoritmo <code>std::sort</code>:
<
std::sort(arr, arr + n_voci, confronta);
</syntaxhighlight>
Ma probabilmente è più efficiente definire la seguente classe di oggetto-funzione (nota anche come ''funtore''):
<
struct Comparatore {
bool operator()(const S& s1, const S& s2) const {
Riga 60:
}
};
</syntaxhighlight>
e passarne un'istanza temporanea all'algoritmo <code>std::sort</code>:
<
std::sort(arr, arr + n_voci, Comparatore());
</syntaxhighlight>
Le funzioni degli oggetti-funzione di solito sono espansi ''inline'', e perciò sono altrettanto efficienti quanto il codice scritto sul posto, mentre le funzioni passate per puntatore vengono raramente espanse ''inline''.
Riga 130:
Per esempio, invece del seguente codice:
<
const char* direzioni[] = { "Nord", "Sud", "Est", "Ovest" };
</syntaxhighlight>
scrivi il seguente codice:
<
enum direzioni { Nord, Sud, Est, Ovest };
</syntaxhighlight>
Un enumerato è implementato come un intero.
Riga 149:
Per esempio, invece del seguente codice:
<
if (a[i] == 1) f();
else if (a[i] == 2) g();
else if (a[i] == 5) h();
</syntaxhighlight>
scrivi il seguente codice:
<
switch (a[i]) {
case 1: f(); break;
Riga 163:
case 5: h(); break;
}
</syntaxhighlight>
I compilatori possono sfruttare la regolarità delle istruzioni <code>switch</code> per applicare alcune ottimizzazioni, in particolare se viene applicata la linea-guida "Valori dei casi di istruzioni <code>switch</code>" di questa sezione.
Riga 175:
Per esempio, il seguente codice C++:
<
switch (i) {
case 10:
Riga 185:
break;
}
</syntaxhighlight>
probabilmente genera del codice macchina corrispondente al seguente pseudo-codice:
<
// N.B.: Questo non è codice C++
static indirizzo a[] = { caso_a, caso_b, fine, caso_a };
Riga 198:
caso_b: funz_b();
fine:
</syntaxhighlight>
Invece, il seguente codice C++:
<
switch (i) {
case 100:
Riga 212:
break;
}
</syntaxhighlight>
probabilmente genera del codice macchina corrispondente al seguente codice:
<
if (i == 100) goto caso_a;
if (i == 130) goto caso_a;
Riga 224:
caso_b: funz_b();
fine:
</syntaxhighlight>
Per così pochi casi, probabilmente non ci sono molte differenze tra le due situazioni, ma con l'aumentare del numero di casi, il primo codice diventa più efficiente, in quanto esegue un solo ''goto calcolato'' invece di una serie di <code>if</code>.
Riga 240:
Esempio: Invece del seguente codice:
<
const int n = 10000;
double a[n], b[n], c[n];
Riga 246:
a[i] = b[i] + c[i];
}
</syntaxhighlight>
scrivi il seguente codice:
<
const int n = 10000;
struct { double a, b, c; } s[n];
Riga 256:
s[i].a = s[i].b + s[i].c;
}
</syntaxhighlight>
In tal modo, i dati da elaborare insieme sono più vicini tra di loro in memoria, e questo permette di ottimizzare l'uso della cache dei dati, e di indirizzare tali dati con istruzioni più compatte, che quindi ottimizzano anche l'uso della cache del codice.
Riga 266:
Per esempio, invece del seguente codice:
<
for (int i = 0; i < 1000; ++i) {
f(i, a1, a2, a3, a4, a5, a6, a7, a8);
}
</syntaxhighlight>
scrivi il seguente codice:
<
struct {
int i;
Riga 285:
f(s);
}
</syntaxhighlight>
Se una funzione riceve pochi argomenti, questi vengono posti direttamente nei registri, e quindi il loro passaggio è molto veloce; ma se gli argomenti non sono pochi, devono essere posti nello stack ad ogni chiamata, anche se non sono cambiati dall'iterazione precedente.
|