Ottimizzare C++/Ottimizzazione del codice C++: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
Nessun oggetto della modifica
Riga 115:
== Come diminuire il numero di istruzioni eseguite ==
 
=== Verifica di intervallo ===
=== 5.3.1. Per controllare se un numero intero “i” è compreso tra “min_i” e “max_i”, estremi inclusi, usa l’espressione “unsigned(i – min_i) <= unsigned(max_i – min_i)”. ===
 
=== 5.3.1. '''Per controllare se un numero intero “i”''i'' è compreso tra “min_i”''min_i'' e “max_i”''max_i'', estremi inclusi, usa l’espressionel'espressione “unsigned''unsigned(i – min_i) <= unsigned(max_i – min_i)''. ==='''
La suddetta formula è equivalente a quella più intuitiva:
 
La suddetta formula è equivalente aalla quellaseguente formula, più intuitiva:
 
<source lang=cpp>min_i <= i && i <= max_i</source>
 
Quest'ultima formula richiede due confronti, mentre quella suggerita ne richiede uno solo.
la quale tuttavia richiede due confronti invece di uno solo. In particolare, per controllare che un numero “i” sia valido come indice per accedere a un array di “size” elementi, si può scrivere l’espressione:
 
la quale tuttavia richiede due confronti invece di uno solo. In particolare, per controllare che un numero “i”''i'' sia valido come indice per accedere a un array di “size”''size'' elementi, si può scrivereusare la seguente l’espressioneespressione:
 
<source lang=cpp>unsigned(i) < unsigned(size)</source>
 
Se le espressioni utilizzate sono già ti un tipo ''unsigned'', le conversioni non sono necessarie.
 
=== Ordine dei casi di ''switch'' ===
=== 5.3.2. Nelle istruzioni switch, poni i casi in ordine di probabilità decrescente. ===
 
=== 5.3.2. '''Nelle istruzioni switch, poni i casi in ordine di probabilità decrescente. ==='''
Nella linea-guida 3.1.12 si consigliava di porre prima di casi più tipici, cioè quelli che si presume siano più probabili. Come ulteriore ottimizzazione, si dovrebbe contare, in esecuzioni tipiche, il numero di volte in cui viene eseguito ognuno dei singoli casi, e porre i casi in ordine da quello eseguito più volte a quello eseguito meno volte.
 
Nella linea-guida 3.1.12 si consigliava di porre prima di casi più tipici, cioè quelli che si presume siano più probabili.
=== 5.3.3. Se un certo valore intero è una costante nel codice applicativo, ma è una variabile nel codice di libreria, rendilo un parametro di template. ===
Nella linea-guida 3.1.12 si consigliava di porre prima di casi più tipici, cioè quelli che si presume siano più probabili. Come ulteriore ottimizzazione, si dovrebbe contare, in esecuzioni tipiche, il numero di volte in cui viene eseguito ognuno dei singoli casi, e porre i casi in ordine da quello eseguito più volte a quello eseguito meno volte.
 
=== Parametri interi di template ===
Supponi che stai scrivendo il seguente template di funzione di libreria, in cui sia x che y non hanno un valore definito in fase di sviluppo della libreria:
 
=== 5.3.3. '''Se un certo valore intero è una costante nel codice applicativo, ma è una variabile nel codice di libreria, rendilo un parametro di template. ==='''
<source lang=cpp>template <typename T> T f1(T x, T y) { return x * y; }</source>
 
Supponi che stai scrivendo illa seguente template di funzione di libreria, in cui sia x che y non hanno un valore definito in fase di sviluppo della libreria:
 
<source lang=cpp>template <typename T> Tint f1(Tint x, Tint y) { return x * y; }</source>
 
Tale funzione può essere chiamata dal seguente codice applicativo, nel quale x non ha un valore costante, ma y è la costante 4:
Line 141 ⟶ 150:
<source lang=cpp>int a = f1(b, 4);</source>
 
Se mentre scrivi la libreria sai che il chiamante ti passerà sicuramente una costante intera come argomento y, puoi trasformare illa templatefunzione nel seguente template di funzione:
Tale chiamata istanzia automaticamente la seguente funzione:
 
<source lang=cpp>inttemplate f1(<int x,Y> int yf2(int x) { return x * yY; }</source>
 
Se mentre scrivi la libreria sai che il chiamante ti passerà sicuramente una costante intera come argomento y, puoi trasformare il template nel seguente:
 
<source lang=cpp>template <typename T, int Y> T f2(T x) { return x * Y; }</source>
 
Tale funzione può essere chiamata dal seguente codice applicativo:
 
<source lang=cpp>int a = f2<int, 4>(b);</source>
 
Tale chiamata istanzia automaticamente la seguente funzione:
Line 157 ⟶ 162:
<source lang=cpp>int f2(int x) { return x * 4; }</source>
 
Questa funzione è più veloce della precedente istanza di ''f1'', per i seguenti motivi:
* Viene passato un solo parametro alla funzione (''x'') invece di due (''x'' e ''y'').
* La divisione per una costante intera (''4'') è più veloce della divisione per una variabile intera (''y'').
* Dato che il valore costante (''4'') è una potenza di due, il compilatore, invece di eseguire una moltiplicazione intera, esegue uno scorrimento di bit a sinistra.
 
In generale, i parametri di template interi sono delle costanti per chi istanzia il template e quindi per il compilatore, e le costanti sono gestite in modo più efficiente delle variabili. Inoltre, alcune operazioni su costanti vengono calcolate in fase di compilazione.
Inoltre, alcune operazioni su costanti vengono calcolate in fase di compilazione.
 
Se invece di avere una funzione avevi già un template di funzione, basta aggiungere un ulteriore parametro a tale template.
 
=== Il ''Curiously Recurring Template Pattern'' ===
In generale, i parametri di template interi sono delle costanti per chi istanzia il template e quindi per il compilatore, e le costanti sono gestite in modo più efficiente delle variabili. Inoltre, alcune operazioni su costanti vengono calcolate in fase di compilazione.
 
=== 5.3.4. '''Se devi scrivere una classe base astratta di libreria tale che in ogni algoritmo nel codice applicativo si userà una sola classe derivata da tale classe base, usa il “Curiously''Curiously Recurring Template Pattern”Pattern''. ==='''
 
Supponi che stai scrivendo la seguente classe base di libreria:
Line 219 ⟶ 229:
* per ogni derivazione di Base tutto il codice di Base viene duplicato.
 
=== Il pattern ''Strategy'' ===
=== 5.3.5. Se un oggetto che implementa il pattern strategy (detto anche policy) è una costante nel codice applicativo, ma il codice di libreria deve poter gestire più strategie, rendi la classe di tale oggetto un parametro di template. ===
 
=== 5.3.5. '''Se un oggetto che implementa il pattern strategy''Strategy'' (detto anche policy''Policy'') è una costante in ogni algoritmo nel codice applicativo, ma il codice di libreria deve poter gestire più strategie, elimina tale oggetto, rendi la''static'' classetutti dii suoi membri, e aggiungi tale oggettoclasse uncome parametro di template. ==='''
Supponi che stai scrivendo il seguente codice di libreria, che implementa il pattern strategy,
 
Supponi che stai scrivendo il seguente codice di libreria, che implementa il design pattern strategy,''Strategy'':
<source lang=cpp>
class C;
Line 238 ⟶ 250:
};
</source>
 
aventeQuesto codice di libreria ha lo scopo di consentire il seguente codice applicativo:
 
<source lang=cpp>
class MyStrategy: public Strategy {
Line 252 ⟶ 266:
c.f(); // Esecuzione con strategia assegnata.
</source>
 
In tal caso, è possibile convertire il precedente codice di libreria nel seguente:
 
<source lang=cpp>
template <class Strategy>
 
class C {
public:
Line 273 ⟶ 288:
c.f(); // Esecuzione con strategia preassegnata.
</source>
 
In tal modo, si evita l'oggetto-strategia, e si ha il binding statico delle funzioni membro MyStrategy::is_valid e MyStrategy::run, cioè si evitano chiamate a funzioni virtuali.
 
Tuttavia, tale soluzione non consente di decidere la strategia in fase di esecuzione, e tanto meno di cambiarla durante la vita dell'oggetto, e, inoltre, duplica il codice della strategia per ogni istanziazione di tale classe.
 
=== Operatori bit-a-bit ===
 
=== 5.3.6. '''Dovendo eseguire operazioni booleane su un insieme di singoli bit, affianca tali bit in una variabile di tipo “unsigned int”, e usa gli operatori bit-a-bit su tale oggetto. ==='''
 
Gli operatori bit-a-bit (''&'', ''|'', ''^'', ''<<'', e ''>>'') sono tradotti in singole istruzioni veloci, e operano su tutti i bit di un registro in una sola istruzione.
 
== 5.4. Come ridurre le costruzioni e distruzioni di oggetti ==