Ottimizzare C++/Scrivere codice C++ efficiente: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
Nessun oggetto della modifica
Riga 5:
== Come approfittare dei costrutti C++ che migliorano le prestazioni ==
 
=== Operatore ''<code>delete''</code> con punattoripuntatori nulli ===
 
'''Non verificare che un puntatore sia non-nullo prima di chiamare <code>delete</code> su di esso.'''
 
Tale controllo viene già fatto da ogni implementazione di <code>delete</code> conforme allo standard, e quindi sarebbe ridondante.
 
=== Funzioni ''<code>qsort''</code> e ''<code>bsearch''</code> ===
 
'''Invece delle funzioni didella libreria ''standard del C <code>qsort''</code> e ''bsearch'', usa le funzioni ''della libreria standard del C++ <code>std::sort''</code> e ''<code>std:lower_bound''</code>.'''
 
Le prime due funzioni richiedono necessariamente un puntatore a funzione, mentre le seconde richiedonopossono usare un oggetto-funzione (o un'espressione lambda).
I puntatori a funzione spesso non sono espansi inline e sono quindi meno efficienti degli oggetti-funzione.
 
=== Funzioni membro ''<code>const''</code> ===
 
'''Dichiara <code>const</code> ogni funzione membro che non modifica lo stato dell'oggetto a cui è applicata.'''
 
Ogni funzione membro riceve l'argomento implicito ''<code>this''</code>.
QuestaIndicare indicazione<code>const</code> una funzione membro equivale a specificare tale argomento implicito come puntatore a ''<code>const''</code>.
 
Per l'uso di ''const'' negli argomenti di funzione, vedi la linea-guida 3.3.5.
La linea-guida 3.3.5 consiglia di usare lo specificatore <code>const</code> negli argomenti di funzione.
 
=== Rappresentazione di simboli ===
Line 33 ⟶ 34:
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> ===
 
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>.
 
I compilatori possono sfruttare la regolarità di tale istruzione per applicare alcune ottimizzazioni, in particolare se viene applicata la linea-guida 3.1.11.
Line 44 ⟶ 45:
 
In fase di progettazione è difficile decidere quale struttura dati avrà prestazioni ottimali nell'uso effettivo dell'applicazione.
In fase di ottimizzazione ci si può accorgere che cambiando il tipo di un contenitore, per esempio passando da ''<code>std::vector''</code> a ''<code>std::list''</code>, si ottengono prestazioni migliori.
Tuttavia, tale modifica comporta la modifica di gran parte del codice sorgente che utilizza direttamente il contenitore che ha cambiato di tipo.
 
Line 56 ⟶ 57:
'''Nell'uso dei contenitori STL, a parità di prestazioni, fa' in modo di rendere intercambiabile il contenitore.'''
 
Per esempio, chiama <code>a.empty()</code> invece di <code>a.size() == 0</code>, e chiama <code>iter != a.end()</code> invece di <code>iter < a.end()</code>.
 
Purtroppo, non è sempre possibile scrivere del codice egualmente efficiente e valido per ogni tipo di contenitore.
Line 63 ⟶ 64:
=== Empressioni lambda ===
 
'''Invece di scrivere un ciclo <code>for</code> su un contenitore STL, usa un algoritmo di STL con un'espressione lambda (usando Boost o C++0x).'''
 
Gli algoritmi di STL sono già dei cicli ottimizzati per gli specifici contenitori, ed evitano il rischio di introdurre operazioni inefficienti.
Line 73 ⟶ 74:
=== Scelta del contenitore di default ===
 
'''Se devi scegliere un contenitore a lunghezza variabile, e sei incerto su quale contenitore scegliere, usa un ''<code>vector''</code>.'''
 
Per insiemi fino a 8 elementi, il <code>vector</code> è il contenitore a lunghezza variabile più efficiente per qualunque operazione.
 
Per insiemi più grandi, altri contenitori possono diventare gradualmente più efficienti a seconda delle operazioni, ma il <code>vector</code> rimane quello che ha minore occupazione di memoria (purché non ci sia capacità in eccesso), minor tempo di scansione completa, e maggiore località di riferimento.
 
=== Funzioni espanse ''<code>inline''</code> ===
 
'''Se usi compilatori che consentono l'ottimizzazione dell'intero programma e l'espansione ''<code>inline''</code> di ogni funzione che il compilatore ritenga opportuno, usa tali opzioni e non dichiarare ''<code>inline''</code> nessuna funzione. Se tali funzionalità del compilatore non fossero disponibili, dichiara ''</code>inline''</code> nei file di intestazione solo le funzioni che contengono non più di tre righe di codice tra le quali non ci sono cicli.'''
 
Le funzioni espanse ''</code>inline''</code> non hanno il costo della chiamata di funzione, che è tanto più grande quanti più sono gli argomenti della funzione. Inoltre, dato che il codice è consecutivo con quello del chiamante, hanno migliore località di riferimento. Infine, dato che il codice intermedio delle funzioni espanse ''</code>inline''</code> si fonde con quello del chiamante, tale codice può essere facilmente ottimizzato dal compilatore.
 
Le funzioni molto piccole, cioè un semplice assegnamento o una semplice istruzione ''<code>return''</code>, quando espanse ''<code>inline''</code> comportano addirittura una riduzione del codice generato complessivo.
 
Tuttavia, ogni volta che viene espansa ''<code>inline''</code> una routine che contiene una quantità notevole di codice, si duplica tale codice, e quindi si ingrandisce la dimensione complessiva del programma, con conseguente rallentamento a causa della minore località di riferimento.
 
Tra le routine non piccolissime, solo quelle critiche per la velocità verranno rese ''inline'' in fase di ottimizzazione.
 
=== 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 lacune.'''
 
I compilatori ottimizzanti, quando compilano un'istruzione ''<code>switch''</code> i cui valori dei casi comprendono 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 in cui inizia il codice di ogni caso, e quando eseguono lo ''<code>switch''</code> usano tale tabella per saltare al codice associato al caso.
 
Per esempio, il seguente codice C++:
Line 150 ⟶ 151:
</source>
 
Per così pochi casi, probabilmente non ci sono molte differenze, ma con l'aumentare del numero di casi la primo codice è più efficiente in quanto esegue un solo ''goto calcolato'' invece di una cascata di ''<code>if''</code>.
 
=== Ordine dei casi didell'istruzione ''<code>switch''</code> ===
 
'''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, per cui nei casi più tipici verrebbero fatti meno confronti.
 
=== Raggruppamento di più array in un array di strutture ===
Line 217 ⟶ 218:
=== Uso dei tipi più efficienti ===
 
'''Per memorizzare in un oggetto dei numeri interi, usa il tipo <code>int</code> o il tipo <code>unsigned int</code>, a meno che sia necessario un tipo più lungo; per memorizzare dei caratteri usa il tipo <code>char</code>, oa meno che serva il tipo <code>wchar_t</code>; e per memorizzare dei numeri a virgola mobile, usa il tipo <code>double</code>, a meno che sia necessario il tipo <code>long double</code>. Se l'oggetto risultante è medio o grande, sostituisci i tipi interi con il tipo intero più piccolo in grado di contenerlo, ma senza usare i ''bit-field'', e sostituisci i tipi a virgola mobile con il tipo <code>float</code>, a meno che sia necessaria maggiore precisione.'''
 
I tipi <code>int</code> e <code>unsigned int</code> sono per definizione quelli più efficienti su qualunque piattaforma.
Line 225 ⟶ 226:
Alcuni tipi di processore elaborano più velocemente gli oggetti di tipo <code>signed char</code>, mentre altre elaborano più velocemente gli oggetti di tipo <code>unsigned char</code>.
Pertanto, in C e in C++ esiste il tipo <code>char</code> che corrisponde al tipo di carattere elaborato più velocemente sul processore target.
 
Il tipo <code>char</code> può contenere solo piccoli insiemi di caratteri; tipicamente fino a un massimo di 255 caratteri distinti.
Per memorizzare set di caratteri più grandi, si deve ricorrere al tipo <code>wchar_t</code>, che ovviamente è meno efficiente.
 
Nel caso di interi contenuti in array medi o grandi, o in collezioni che si presume saranno tipicamente medie o grandi, è meglio minimizzare la dimensione in byte della collezione.
Line 238 ⟶ 242:
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>find''</code>, o la funzione membro ''<code>find''</code>; ma il primo ha complessità lineare (O(n)), mentre la seconda ha complessità logaritmica (O(log(n))).
 
=== Ricerca in sequenze ordinate ===
 
'''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>.'''
 
Dato che tutti i citati algoritmi usano la ricerca binaria di complessità logaritmica (O(log(n))), sono più veloci dell'algoritmo ''<code>find''</code>, che usa la ricerca sequenziale di complessità lineare (O(n)).
 
== Come evitare i costi di costrutti C++ che peggiorano le prestazioni ==
Line 254 ⟶ 258:
Altri costrutti sono alquanto inefficienti, e devono quindi essere usati con grande parsimonia.
 
=== UsoL'operatore di ''<code>throw''</code> ===
 
'''Chiama l''operatore <code>throw''</code> solamente quando si dovrà avvisare l'utente del fallimento del comando corrente.'''
 
Il sollevamento di una eccezione ha un costo molto elevato, dell'ordine dei 10000 cicli di processore.
Line 262 ⟶ 266:
Se invece la si effettua come operazione algoritmica, anche se pensata inizialmente per essere eseguita raramente, potrebbe finire per essere eseguita frequentemente.
 
=== Derivazione ''<code>virtual''</code> ===
 
'''Non usare sistematicamente la derivazione ''<code>virtual''</code>, ma solo quando due o più classi devono condividere la rappresentazione di una classe base comune.'''
 
Le funzioni membro delle classi base derivate in modo ''<code>virtual''</code> sono un po' più lente delle funzioni membro derivate in modo non-<code>virtual</code>.
 
Per esempio, considera le seguenti definizioni di classe:
Line 278 ⟶ 282:
Con tali definizioni, ogni oggetto di classe C contiene due oggetti distinti di classe A, uno facente parte della classe base B1, e l'altro facente parte della classe base B2.
 
Questo non costituisce un problema se la classe A non ha nessuna variabile membro non-<code>static</code>.
 
Se invece tale oggetto di classe A contiene qualche variabile membro, e si intende che debba essere unico per ogni oggetto di classe C, si deve usare la derivazione <code>virtual</code>, nel seguente modo:
 
<source lang=cpp>
Line 289 ⟶ 293:
</source>
 
Questa situazione è l'unica in cui è necessaria la derivazione ''<code>virtual''</code>.
 
=== Funzioni membro ''<code>virtual''</code> ===
 
'''In ogni classe, non definire sistematicamente ''<code>virtual''</code> tutte le funzioni membro, ma solo le funzioni membro di cui prevedi la necessità di una ridefinizione, a parte il distruttore, il quale deve essere definito ''<code>virtual''</code> se e solo se la classe contiene almeno un'altra funzione membro ''<code>virtual''</code>.'''
 
A parità di condizioni, le classi che contengono almeno una funzione membro ''<code>virtual''</code> occupano un po' più spazio delle classi che non ne contengono, e gli oggetti delle classi che contengono almeno una funzione membro ''<code>virtual''</code> occupano un po' più spazio e la loro costruzione richiede un po' più di tempo rispetto agli oggetti di classi che non contengono funzioni membro ''<code>virtual''</code>.
Le funzioni membro ''virtual'' occupano po' più spazio e sono un po' più lente da chiamare delle funzioni membro non-''virtual''.
 
Le funzioni membro ''<code>virtual''</code> occupano po' più spazio e sono un po' più lente da chiamare delle funzioni membro non-''<code>virtual''</code>.
=== Funzioni membro ''static'' ===
 
=== Funzioni membro ''<code>static''</code> ===
'''In ogni classe, dichiara ''static'' ogni funzione membro che non accede ai membri ''non-static'' di tale classe.'''
 
'''In altreogni paroleclasse, dichiara ''<code>static''</code> tutteogni le funzionifunzione membro che puoinon accede ai membri non-<code>static</code> di tale classe.'''
 
In altre parole, dichiara <code>static</code> tutte le funzioni membro che puoi.
In questo modo, non viene passato l'argomento implicito ''this''.
 
In questo modo, non viene passato l'argomento implicito ''<code>this''</code>.
 
=== Template di classi polimorfiche ===
Line 312 ⟶ 317:
I template di classe, ogni volta che vengono istanziati, producono una copia del codice oggetto, e se contengono funzioni virtuali producono una copia della ''vtable'' e della ''RTTI''. Questi dati ingrandiscono eccessivamente il programma.
 
=== Annullamento dell'argomento di ''<code>delete''</code> ===
 
'''Non annullare un puntatore dopo aver chiamato ''<code>delete''</code> su di esso, se sei sicuro che tale puntatore non verrà più usato.'''
 
L'uso più tipico dell'operatore ''<code>delete''</code> si ha quando in un distruttore si dealloca un oggetto posseduto dall'oggetto in corso di distruzione; in tale contesto, si chiama ''<code>delete''</code> su una variabile membro di tipo puntatore.
 
Dato che solitamente un distruttore non è così complicato da faticare a capire quali parti dell'oggetto corrente sono già state distrutte e quali non ancora, e dato che il puntatore usato per la chiamata a ''<code>delete''</code> cesserà di esistere alla fine del distruttore stesso, anche se si lascia il valore corrente del puntatore non più valido, non si corre il rischio di usarlo accidentalmente.
 
D'altra parte, annullare il puntatore richiede una seppur piccolissima quantità di tempo.
 
Come tecnica di collaudo o di debugging, si puo'può adottare la regola di annullare sempre il puntatore a cui è stato applicato l'operatore ''<code>delete''</code> nella generazione di una versione finalizzata al collaudo o debugging, ma farlo anche nella generazione del codice di produzione è un'inutile inefficienza.
 
=== Uso di deallocatori automatici ===
Line 328 ⟶ 333:
'''Non usare una libreria di garbage-collection né gli smart-pointer con reference-count (boost::shared_ptr), a meno che se ne dimostri l’opportunità per il caso specifico.'''
 
La garbage collection, cioè il recupero automatico della memoria non più referenziata, fornisce la comodità di non doversi occupare della deallocazione della memoria, e previene i ''memory leak''. Tale funzionalità non viene fornita dalla libreria standard, ma viene fornita da librerie non-standard.
Tuttavia, tale tecnica di gestione della memoria offre prestazioni peggiori della deallocazione esplicita.
 
La libreria standard del C++98 contiene un solo smart-pointer, l'''<code>auto_ptr''</code>, che è efficiente. Altri smart-pointer sono forniti da librerie non-standard, come Boost, o verranno forniti dal C++0x. Tra di essi, gli smart-pointer basati su reference-count, come lo <code>shared_ptr</code> di Boost, sono meno efficienti, e quindi devono essere usati sono nei casi in cui se ne dimostra la necessità. In particolare, compilando con l'opzione di gestione del multithreading, tali funzionalità hanno pessime prestazioni, in quanto devono garantire l'atomicità delle operazioni.
 
Normalmente bisognerebbe, in fase di progettazione, cercare di assegnare ogni oggetto ad un proprietario, che avrà la responsabilità di distruggerlo.
Solo quando tale assegnazione è difficile, in quanto più oggetti tendono a rimpallarsi la responsabilità di distruggere un oggetto, risulta opportuno usare uno smart-pointer con reference-count oppure una libreria di garbage-collection.
 
=== Il modificatore ''<code>volatile''</code> ===
 
'''Non definire sistematicamente ''<code>volatile''</code> ogni variabile, ma solo quelle che vengono modificate in modo asincrono da dispositivi hardware o da altri thread.'''
 
L'uso del modificatore ''<code>volatile''</code> impedisce al compilatore di allocare una variabile in un registro.
Questo garantisce che tutti i dispositivi e tutti i thread ''vedano'' la stessa variabile, ma rende molto più lente le operazioni che manipolano tale variabile.
 
== Come evitare inutili costruzioni e le distruzioni di oggetti ==
 
=== Ambito delle variabili ===
Line 350 ⟶ 356:
Dichiarare una variabile il più tardi possibile, significa sia dichiararla nell'ambito più stretto possibile, sia dichiararla il più avanti possibile entro quell'ambito.
Essere nell'ambito più stretto possibile comporta che se tale ambito non viene mai eseguito, l'oggetto associato alla variabile non viene mai costruito né distrutto.
Essere il più avanti possibile all’interno di un ambito comporta che se prima di tale dichiarazione c'è un'uscita prematura, tramite ''<code>return''</code> o ''<code>break''</code> o ''<code>continue''</code>, l’oggettol'oggetto associato alla variabile non viene mai costruito né distrutto.
 
Inoltre, spesso all'inizio di una routine non si ha un valore appropriato per inizializzare l'oggetto associato alla variabile, e quindi si è costretti a inizializzarla con un valore di default, e poi assegnarle il valore appropriato.
Line 360 ⟶ 366:
 
Se un oggetto di classe non viene inizializzato esplicitamente, viene comunque inizializzato automaticamente dal costruttore di default.
In generale, chiamare il costruttore di default seguito da un assegnamento di un valore è meno efficiente o ugualmeneugualmente efficiente che chiamare solo un costruttore con tale valore.
 
=== Operatori di incremento/decremento ===
 
'''Usa gli operatori prefissi di incremento (<code>++</code>) e decremento (<code>--</code>) invece dei corrispondenti operatori postfissi, se il valore dell'espressione non viene usato.'''
 
Se l'oggetto incrementato è di un tipo fondamentale, non ci sono differenze tra le due forme, ma se si tratta di un tipo composito, l'operatore postfisso comporta la creazione di un inutile oggetto temporaneo, mentre l'operatore prefisso no.
Line 374 ⟶ 380:
=== Operatori compositi di assegnamento ===
 
'''Usa gli operatori compositi di assegnamento (come in ''<code>a += b''</code>) invece degli operatori semplici combinati con operatori di assegnamento (come in ''<code>a = a + b''</code>).'''
 
Tipicamente un operatore semplice, come nell'espressione ''a + b'', crea un oggetto temporaneo.
Line 395 ⟶ 401:
=== Passaggio di argomenti alle funzioni ===
 
'''Quando devi passare un argomento ''<code>x''</code> di tipo ''<code>T''</code> a una funzione, usa il seguente criterio:'''
Se ''<code>x''</code> è un argomento di solo input,
se ''<code>x''</code> può essere nullo,
passalo per puntatore a costante (''<code>const T* x''</code>),
altrimenti, se ''<code>T''</code> è un tipo fondamentale o un iteratore o un oggetto-funzione,
passalo per valore (''<code>T x''</code>) o per valore costante (''<code>const T x''</code>),
altrimenti,
passalo per riferimento a costante (''<code>const T& x''</code>),
altrimenti, cioè se ''<code>x''</code> è un argomento di solo output o di input/output,
se ''<code>x''</code> può essere nullo,
passalo per puntatore a non-costante (''<code>T* x''</code>),
altrimenti,
passalo per riferimento a non-costante (''<code>T& x''</code>).
 
Il passaggio per riferimento è più efficiente del passaggio per puntatore in quanto facilita al compilatore l’eliminazione della variabile, e in quanto il chiamato non deve verificare se il riferimento è valido o nullo; tuttavia, il puntatore ha il pregio di poter rappresentare un valore nullo, ed è più efficiente passare solo un puntatore, che un riferimento a un oggetto insieme a un booleano che indica se tale riferimento è valido.
 
Per oggetti che possono essere contenuti in uno o due registri, il passaggio per valore è più efficiente o ugualmente efficiente del passaggio per riferimento, in quanto tali oggetti possono essere contenuti in registri e non hanno livelli di indirettezza, pertanto questo è il modo più efficiente di passare oggetti sicuramente piccoli, come i tipi fondamentali, gli iteratori e gli oggetti-funzione.
Per oggetti più grandi di due registri, il passaggio per riferimento è più efficiente del passaggio per valore, in quanto tali oggetti non devono essere copiati.
 
Un oggetto composito veloce da copiare potrebbe essere efficientemente passato per valore, ma, a meno che si tratti di un iteratore o di un oggetto-funzione, per i quali si assume l’efficienza della copia, tale tecnica è rischiosa, in quanto l’oggetto potrebbe diventare in futuro più lento da copiare.
Per esempio, se un oggetto di classe <code>Point</code> contiene solo due <code>float</code>, potrebbe essere efficientemente passato per valore; ma se in futuro si aggiungeaggiungesse un terzo <code>float</code>, o se i due <code>float</code> diventanodiventassero due <code>double</code>, potrebbe diventare più efficiente il passaggio per riferimento.
 
=== DiciarazioneDichiarazione ''<code>explicit''</code> ===
 
'''Dichiara ''<code>explicit''</code> tutti i costruttori che possono ricevere un solo argomento, eccetto i costruttori di copia delle classi concrete.'''
 
I costruttori impliciti possono essere chiamati automaticamente dal compilatore che esegue una conversione automatica. A seconda della complessità del costruttore, tale chiamata può richiedere molto più tempo del necessario. Rendendo obbligatoriamente esplicita tale conversione, il compilatore potrebbe scegliere un’altraun'altra funzione in overload, evitando così di chiamare il costruttore, oppure segnalare errore e costringere il programmatore a scegliere un’altra strada per evitare la chiamata al costruttore.
 
Per i costruttori di copia delle classi concrete si deve fare eccezione, per consentirne il passaggio per valore. Per le classi astratte, anche i costruttori di copia possono essere dichiarati ''<code>explicit''</code>, in quanto, per definizione, le classi astratte non si possono istanziare, e quindi gli oggetti di tale tipo non dovrebbero mai essere passati per valore.
 
=== Operatori di conversione ===
 
'''Non dichiarare operatori di conversione, se non per mantenere la compatibilità con una libreria obsoleta (in C++0x, dichiarali explicit).'''
 
Gli operatori di conversioni consentono conversioni implicite, e quandiquindi incorrono nello stesso problema dei costruttori impliciti.
 
Se tali conversioni sono necessarie, fornisci invece una funzione membro equivalente, che può essere chiamata solo esplicitamente.
 
L'unico utilizzo che rimane accettabile per gli operatori di conversione si ha quando si sta convertendo una grande mole di software dall'uso di una libreria all'uso di un'altra libreria simile. Durante la fase di transizione, in cui le due librerie coesistono, può essere comodo avere operatori che convertono automaticamente gli oggetti dai tipi della vecchia libreria ai tipi della nuova libreria.
 
=== Idioma ''Pimpl'' ===
Line 435 ⟶ 445:
'''Non usare sistematicamente l'idioma ''Pimpl'', ma solo quando vuoi rendere il resto del programma indipendente dall'implementazione di una classe.'''
 
L'idioma ''Pimpl'' (che significa Puntatore a IMPLementazione) consiste nel memorizzare nell'oggetto solamente un puntatore alla struttura che contiene tutte le informazioni utili di tale oggetto.
 
Il vantaggio principale di tale idioma è che velocizza la compilazione incrementale del codice, cioè rende meno probabile che una piccola modifica ai sorgenti comporti la necessità di ricompilare grandi quantità di codice.
 
Tale idioma consente anche di velocizzare alcune operazioni, come lo <code>swap</code> tra due oggetti, ma in generale rallenta gli accessi ai dati dell'oggetto a causa del livello di indirettezza, e provoca un'allocazione aggiuntiva per ogni creazione e copia di tale oggetto.
Quindi non dovrebbe essere usato per classi le cui funzioni membro pubbliche sono chiamate frequentemente.
 
=== Iteratori e oggetti_funzioneoggetti-funzione ===
 
'''Nelle classi di iteratori o di oggetti-funzione, fa' in modo che tali oggetti siano piccolissimi e che non allochino memoria dinamica.'''
Line 455 ⟶ 465:
Inoltre, tale tipo di allocazione comporta uno spreco di spazio per ogni allocazione, genera frammentazione della memoria virtuale, e produce una scarsa località dei dati, con conseguente scadente utilizzo sia delle cache dei dati della CPU che della memoria virtuale.
 
Tale allocazione/deallocazione in linguaggio C veniva fatta con le funzioni malloc e free. In C++, pur essendo ancora disponibili tali funzioni, le funzioni normalmente usate a tale scopo sono gli operatori <code>new</code>, <code>new[]</code>, <code>delete</code>, e <code>delete[]</code>.
 
Ovviamente, un modo di ridurre le allocazioni è ridurre il numero di oggetti costruiti, e quindi la sezione “Come evitare inutili costruzioni e le distruzioni di oggetti” serve indirettamente anche allo scopo di questa sezione.
Line 463 ⟶ 473:
=== Array di lunghezza fissa ===
 
'''Se un array statico o non grande ha lunghezza costante, non usare un oggetto <code>vector</code>, ma usa un array del C, o un oggetto <code>boost:array</code>.'''
 
I vector memorizzano i dati in un buffer allocato dinamicamente, mentre le altre soluzioni proposte allocano i dati nell'oggetto stesso.
Line 476 ⟶ 486:
Un ''allocatore a blocchi'' (detto anche ''allocatore a pool'') alloca blocchi di memoria medi o grandi, e fornisce servizi di allocazione/deallocazione di blocchi più piccoli di dimensione costante, offrendo alta velocità di allocazione/deallocazione, bassa frammentazione della memoria, uso efficiente delle cache dei dati e della memoria virtuale.
 
In particolare, un allocatore di questo tipo migliora notevolmente le prestazioni dei contenitori ''<code>std::list''</code>, ''<code>std::set''</code>, ''<code>std::multi_set''</code>, ''<code>std::map''</code>, e ''<code>std::multi_map''</code>.
 
Se la tua implementazione della libreria standard non usa già un allocatore a blocchi per questi contenitori, dovresti procurartene uno (per esempio, questo: http://www.codeproject.com/KB/stl/blockallocator.aspx), e specificarlo come parametro di template per le istanze di tali template di contenitori.
Line 482 ⟶ 492:
=== Aggiunta di elementi a collezione ===
 
'''Per aggiungere elementi in fondo a una collezione, usa ''<code>push_back''</code> per aggiungere un singolo elemento, ''<code>back_inserter''</code> per far aggiungere elementi a un algoritmo STL, ''<code>insert''</code> per inserire una sequenza.'''
 
La funzione ''<code>push_back''</code> garantisce un tempo lineare ammortizzato, in quanto, nel caso del ''<code>vector''</code>, ingrandisce esponenzialmente la capacità.
 
La classe ''<code>back_inserter''</code> chiama internamente la funzione ''<code>push_back''</code>.
 
La funzione ''<code>insert''</code> permette di inserire in modo ottimizzato un'intera sequenza, e quindi una chiamata di questo tipo è più veloce di numerose chiamate a ''<code>push_back''</code>.
 
== Come velocizzare l'accesso alla memoria principale ==
Line 513 ⟶ 523:
'''Lascia l'allineamento di memoria suggerito dal compilatore.'''
 
I compilatori attivano di default un criterio di allineamento dei tipi fondamentali, per cui le variabili di ogni tipo possono iniziare solo a determinati indirizzi di memoria.
Tale criterio solitamente garantisce le massime prestazioni, ma può introdurre degli spazi inutilizzati tra le variabili.
Se per alcune strutture è necessario eliminare tali spazi, usa le direttiva ''pragma'' per confinare tale impaccamento alle sole strutture per cui è necessario.
 
=== Raggruppamento di funzioni in unità di compilazione ===
 
'''Definisci nella stessa unità di compilazione tutte le funzioni membro di una classe, le funzioni ''<code>friend''</code> di tale classe e le funzioni delle classi ''<code>friend''</code> di tale classe, a meno che il file risultante diventi scomodo da gestire per la sua dimensione eccessiva.'''
 
In tal modo, sia il codice macchina prodotto compilando tali routine sia i dati statici definiti in tali classi e routine avranno indirizzi vicini tra loro; inoltre, così si consente ai compilatori che non effettuano ottimizzazioni sull'intero programma di ottimizzare le chiamate tra tali funzioni.
Line 533 ⟶ 545:
In tal modo, si dichiara che non verranno usate da altre unità di compilazione. Questo permette ai compilatori che non effettuano ottimizzazioni sull'intero programma di ottimizzare l'utilizzo di tali variabili e funzioni.
 
== QuandoUso usare idei thread ==
 
=== Thread di lavoro ===