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:
 
<sourcesyntaxhighlight lang=cpp>
struct S {
int a, b;
};
S arr[n_voci];
</syntaxhighlight>
</source>
 
e vuoi ordinarlo in base al campo <code>b</code>, potresti definire la seguente funzione di confronto:
 
<sourcesyntaxhighlight lang=cpp>
bool confronta(const S& s1, const S& s2) {
return s1.b < s2.b;
}
</syntaxhighlight>
</source>
 
e passarla all'algoritmo <code>std::sort</code>:
 
<sourcesyntaxhighlight lang=cpp>
std::sort(arr, arr + n_voci, confronta);
</syntaxhighlight>
</source>
 
Ma probabilmente è più efficiente definire la seguente classe di oggetto-funzione (nota anche come ''funtore''):
 
<sourcesyntaxhighlight lang=cpp>
struct Comparatore {
bool operator()(const S& s1, const S& s2) const {
Riga 60:
}
};
</syntaxhighlight>
</source>
 
e passarne un'istanza temporanea all'algoritmo <code>std::sort</code>:
 
<sourcesyntaxhighlight lang=cpp>
std::sort(arr, arr + n_voci, Comparatore());
</syntaxhighlight>
</source>
 
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:
 
<sourcesyntaxhighlight lang=cpp>
const char* direzioni[] = { "Nord", "Sud", "Est", "Ovest" };
</syntaxhighlight>
</source>
 
scrivi il seguente codice:
 
<sourcesyntaxhighlight lang=cpp>
enum direzioni { Nord, Sud, Est, Ovest };
</syntaxhighlight>
</source>
 
Un enumerato è implementato come un intero.
Riga 149:
Per esempio, invece del seguente codice:
 
<sourcesyntaxhighlight lang=cpp>
if (a[i] == 1) f();
else if (a[i] == 2) g();
else if (a[i] == 5) h();
</syntaxhighlight>
</source>
 
scrivi il seguente codice:
 
<sourcesyntaxhighlight lang=cpp>
switch (a[i]) {
case 1: f(); break;
Riga 163:
case 5: h(); break;
}
</syntaxhighlight>
</source>
 
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++:
 
<sourcesyntaxhighlight lang=cpp>
switch (i) {
case 10:
Riga 185:
break;
}
</syntaxhighlight>
</source>
 
probabilmente genera del codice macchina corrispondente al seguente pseudo-codice:
 
<sourcesyntaxhighlight lang=cpp>
// N.B.: Questo non è codice C++
static indirizzo a[] = { caso_a, caso_b, fine, caso_a };
Riga 198:
caso_b: funz_b();
fine:
</syntaxhighlight>
</source>
 
Invece, il seguente codice C++:
 
<sourcesyntaxhighlight lang=cpp>
switch (i) {
case 100:
Riga 212:
break;
}
</syntaxhighlight>
</source>
 
probabilmente genera del codice macchina corrispondente al seguente codice:
 
<sourcesyntaxhighlight lang=cpp>
if (i == 100) goto caso_a;
if (i == 130) goto caso_a;
Riga 224:
caso_b: funz_b();
fine:
</syntaxhighlight>
</source>
 
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:
 
<sourcesyntaxhighlight lang=cpp>
const int n = 10000;
double a[n], b[n], c[n];
Riga 246:
a[i] = b[i] + c[i];
}
</syntaxhighlight>
</source>
 
scrivi il seguente codice:
 
<sourcesyntaxhighlight lang=cpp>
const int n = 10000;
struct { double a, b, c; } s[n];
Riga 256:
s[i].a = s[i].b + s[i].c;
}
</syntaxhighlight>
</source>
 
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:
 
<sourcesyntaxhighlight lang=cpp>
for (int i = 0; i < 1000; ++i) {
f(i, a1, a2, a3, a4, a5, a6, a7, a8);
}
</syntaxhighlight>
</source>
 
scrivi il seguente codice:
 
<sourcesyntaxhighlight lang=cpp>
struct {
int i;
Riga 285:
f(s);
}
</syntaxhighlight>
</source>
 
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.