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

Wikibooks, manuali e libri di testo liberi.
Contenuto cancellato Contenuto aggiunto
RamaccoloBot (discussione | contributi)
Riportate modifiche da en.wikibooks
 
Riga 1: Riga 1:
{{Ottimizzare C++}}
{{Ottimizzare C++}}


L'allocazione e la deallocazione di memoria dinamica sono operazioni molto lente, se confrontate con l’allocazione e la deallocazione di memoria automatica, cioè su stack.
L'allocazione e la deallocazione dinamiche di memoria sono operazioni molto lente, a confronto dell'allocazione e della deallocazione automatiche di memoria.
In altre parole, lo ''heap'' è molto più lento dello ''stack''.


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.
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 del processore che dello spazio di memoria virtuale.


Tale allocazione/deallocazione veniva fatta in linguaggio C con le funzioni <code>malloc</code> e <code>free</code>.
L'allocazione/deallocazione dinamica di memoria veniva fatta in linguaggio C usando le funzioni <code>malloc</code> e <code>free</code> della libreria standard.
In C++, pur essendo ancora disponibili tali funzioni, normalmente a tale scopo si usano gli operatori <code>new</code>, <code>new[]</code>, <code>delete</code>, e <code>delete[]</code>.
In C++, pur essendo ancora disponibili tali funzioni, normalmente a tale scopo si usano gli operatori <code>new</code>, <code>new[]</code>, <code>delete</code>, e <code>delete[]</code>.


Riga 14: Riga 15:
=== Array di lunghezza fissa ===
=== 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>array</code> della libreria [http://www.boost.org Boost].'''
'''Se un array statico o non grande ha lunghezza costante, invece di usare un oggetto <code>vector</code>, usa un array del C, o un oggetto <code>array</code> della libreria [http://www.boost.org Boost].'''


I <code>vector</code> memorizzano i dati in un buffer allocato dinamicamente, mentre le altre soluzioni proposte allocano i dati nell'oggetto stesso.
I <code>vector</code> memorizzano i dati in un buffer allocato dinamicamente, mentre le altre soluzioni proposte allocano i dati nell'oggetto stesso.
Questo consente di evitare allocazioni/deallocazioni di memoria dinamica e di favorire la località dei dati.
Questo consente di evitare allocazioni/deallocazioni di memoria dinamica e di favorire la località dei dati.


Se l'array è medio o grande, tali vantaggi diminuiscono, e invece risulta più importante evitare di usare troppo spazio sullo stack.
Se l'array è grande, tali vantaggi diminuiscono, e invece risulta più importante evitare di usare troppo spazio sullo stack.


=== Allocatore a blocchi ===
=== Allocatore a blocchi ===
Riga 25: Riga 26:
'''Se devi allocare numerosi blocchi di memoria della stessa dimensione, assicurati di usare un allocatore a blocchi.'''
'''Se devi allocare numerosi blocchi di memoria della stessa dimensione, assicurati di usare un allocatore a blocchi.'''


Un [[w:en:Memory pool|''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.
Un [[w:en:Memory pool|''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. Offre 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>.
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>.
Riga 35: Riga 36:
'''Quando aggiungi elementi in fondo a una collezione, usa <code>push_back</code> per aggiungere un singolo elemento, usa <code>insert</code> per aggiungere una sequenza, e usa <code>back_inserter</code> per fare in modo che un algoritmo STL aggiunga elementi a una sequenza.'''
'''Quando aggiungi elementi in fondo a una collezione, usa <code>push_back</code> per aggiungere un singolo elemento, usa <code>insert</code> per aggiungere una sequenza, e usa <code>back_inserter</code> per fare in modo che un algoritmo STL aggiunga elementi a 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 funzione <code>push_back</code> garantisce un tempo lineare ammortizzato, in quanto, nel caso dei <code>vector</code>, ingrandisce esponenzialmente la capacità.


La classe <code>back_inserter</code> chiama internamente la funzione <code>push_back</code>.
La classe <code>back_inserter</code> chiama internamente la funzione <code>push_back</code>.

Versione attuale delle 19:13, 17 giu 2008

Indice del libro

L'allocazione e la deallocazione dinamiche di memoria sono operazioni molto lente, a confronto dell'allocazione e della deallocazione automatiche di memoria. In altre parole, lo heap è molto più lento dello stack.

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 del processore che dello spazio di memoria virtuale.

L'allocazione/deallocazione dinamica di memoria veniva fatta in linguaggio C usando le funzioni malloc e free della libreria standard. In C++, pur essendo ancora disponibili tali funzioni, normalmente a tale scopo si usano gli operatori new, new[], delete, e delete[].

Ovviamente, un modo di ridurre le allocazioni è ridurre il numero di oggetti costruiti, e quindi la sezione "Costruzioni e distruzioni" di questo capitolo serve indirettamente anche allo scopo di questa sezione.

Tuttavia, qui si presenteranno linee-guida per ridurre il numero di allocazioni di memoria per un dato numero di chiamate all'operatore new.

Array di lunghezza fissa[modifica]

Se un array statico o non grande ha lunghezza costante, invece di usare un oggetto vector, usa un array del C, o un oggetto array della libreria Boost.

I vector memorizzano i dati in un buffer allocato dinamicamente, mentre le altre soluzioni proposte allocano i dati nell'oggetto stesso. Questo consente di evitare allocazioni/deallocazioni di memoria dinamica e di favorire la località dei dati.

Se l'array è grande, tali vantaggi diminuiscono, e invece risulta più importante evitare di usare troppo spazio sullo stack.

Allocatore a blocchi[modifica]

Se devi allocare numerosi blocchi di memoria della stessa dimensione, assicurati di usare un allocatore a blocchi.

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. Offre 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 std::list, std::set, std::multi_set, std::map, e std::multi_map.

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.

Aggiunta di elementi a collezione[modifica]

Quando aggiungi elementi in fondo a una collezione, usa push_back per aggiungere un singolo elemento, usa insert per aggiungere una sequenza, e usa back_inserter per fare in modo che un algoritmo STL aggiunga elementi a una sequenza.

La funzione push_back garantisce un tempo lineare ammortizzato, in quanto, nel caso dei vector, ingrandisce esponenzialmente la capacità.

La classe back_inserter chiama internamente la funzione push_back.

La funzione insert permette di inserire in modo ottimizzato un'intera sequenza, e quindi una sola chiamata di questo tipo è più veloce di numerose chiamate a push_back.