Ottimizzare C++/Scrivere codice C++ efficiente/Costrutti che migliorano le prestazioni: differenze tra le versioni
Contenuto cancellato Contenuto aggiunto
Riportate modifiche da en.wikibooks |
Riportate modifiche da en.wikibooks |
||
Riga 127:
'''Per rappresentare dei simboli interni, usa degli enumerati, invece di stringhe.'''
<source lang=cpp>
const char* direzioni[] = { "Nord", "Sud", "Est", "Ovest" };
</source>
scrivi il seguente codice:
<source lang=cpp>
enum direzioni { Nord, Sud, Est, Ovest };
</source>
Un enumerato è implementato come un intero.
Tipicamente, rispetto ad un intero, una stringa occupa più spazio, ed è più lenta da copiare e da confrontare.
=== Istruzioni <code>if</code> e <code>switch</code> ===
Line 136 ⟶ 147:
Se devi confrontare un valore intero con una serie di valori costanti, invece di una serie di istruzioni <code>if</code>, usa un'istruzione <code>switch</code>.
Per esempio, invece del seguente codice:
I compilatori possono sfruttare la regolarità di tale istruzione per applicare alcune ottimizzazioni, in particolare se viene applicata la linea-guida "Valori dei casi di istruzioni <code>switch</code>" di questa sezione.▼
<source lang=cpp>
if (a[i] == 1) f();
else if (a[i] == 2) g();
else if (a[i] == 5) h();
</source>
scrivi il seguente codice:
<source lang=cpp>
switch (a[i]) {
case 1: f(); break;
case 2: g(); break;
case 5: h(); break;
}
</source>
▲I compilatori possono sfruttare la regolarità
=== Valori dei casi di istruzioni <code>switch</code> ===
'''Come costanti per i casi delle istruzioni <code>switch</code>, usa sequenze compatte di valori, cioè senza lacune o con poche piccole lacune.'''
I compilatori ottimizzanti, quando compilano un'istruzione <code>switch</code> i cui valori dei casi costituiscono la maggior parte dei valori interi compresi in un intervallo, invece di generare una sequenza di istruzioni <code>if</code>, generano una ''jump-table'', ossia un array degli indirizzi a cui inizia il codice di ogni caso, e
Per esempio, il seguente codice C++:
Line 207 ⟶ 226:
</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
=== Ordine dei casi dell'istruzione <code>switch</code> ===
Line 213 ⟶ 232:
'''Nelle istruzioni <code>switch</code>, poni prima i casi più tipici.'''
Se il compilatore non generasse la ''jump-table'', i casi verrebbero confrontati in ordine di comparizione; pertanto,
=== Raggruppamento di più array in un solo array di strutture ===
'''Invece di elaborare in parallelo due o più array della stessa lunghezza, elabora un solo array di oggetti
Esempio: Invece del seguente codice:
Line 239 ⟶ 258:
</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.
=== Raggruppamento di argomenti di funzione ===
'''Se una funzione riceve almeno sei argomenti, e viene chiamata in un ciclo con gli stessi valori tranne al più due, prima del ciclo crea una struttura che contiene tutti gli argomenti, e passa alla funzione tale struttura
Per esempio, invece del seguente codice:
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, anche se sono gli stessi della precedente chiamata alla stessa funzione.▼
Se invece si passa solo l'indirizzo di una struttura, questo indirizzo viene sicuramente posto in un registro, e i campi della struttura che non sono modificati tra chiamate successive della funzione devono essere assegnati solo la prima volta.▼
▲Per esempio, rispetto al seguente codice:
<source lang=cpp>
Line 257 ⟶ 272:
</source>
scrivi il seguente codice
<source lang=cpp>
Line 272 ⟶ 287:
</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
▲Se invece si passa alla funzione solo l'indirizzo di una struttura, questo indirizzo viene
=== Uso di funzioni membro di contenitori ===
Line 277 ⟶ 295:
'''Per cercare un elemento in un contenitore, usa una funzione membro del contenitore, invece di un algoritmo STL.'''
Se è stata creata una tale funzione membro specifica, quando esisteva già un algoritmo STL generico, è solo perché tale funzione membro è più efficiente.
Per esempio, per cercare in un oggetto <code>std::set</code>, si può usare l'algoritmo generico <code>std::find</code>, o la funzione membro <code>std::set::find</code>; ma il primo ha complessità lineare (O(n)), mentre la seconda ha complessità logaritmica (O(log(n))).
=== Ricerca in sequenze ordinate ===
Line 285 ⟶ 303:
'''Per cercare un elemento in una sequenza ordinata, usa gli algoritmi <code>lower_bound</code>, <code>upper_bound</code>, <code>equal_range</code>, o <code>binary_search</code>.'''
[[Categoria:Ottimizzare C++|Costrutti che migliorano le prestazioni]]
|