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)
m Bot: Sostituzione automatica (-[[Categoria:Ottimizzare C++|Ottimizzare C++/Scrivere codice C++ efficiente/ +[[Categoria:Ottimizzare C++|)
Nessun oggetto della modifica
Riga 5: Riga 5:
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 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>.
Tale allocazione/deallocazione veniva fatta in linguaggio C con le funzioni <code>malloc</code> e <code>free</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>.


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.
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 regole per ridurre il numero di allocazioni di memoria per un dato numero di chiamate all'operatore new.
Tuttavia, qui si presenteranno linee-guida per ridurre il numero di allocazioni di memoria per un dato numero di chiamate all'operatore <code>new</code>.


=== 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>boost:array</code>.'''
'''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].'''


I vector 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.


Riga 24: Riga 25:
'''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 ''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, 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>.
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 32: Riga 33:
=== Aggiunta di elementi a collezione ===
=== 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.'''
'''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 del <code>vector</code>, ingrandisce esponenzialmente la capacità.
Riga 38: Riga 39:
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>.


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>.
La funzione <code>insert</code> permette di inserire in modo ottimizzato un'intera sequenza, e quindi una sola chiamata di questo tipo è più veloce di numerose chiamate a <code>push_back</code>.


[[Categoria:Ottimizzare C++|Allocazioni e deallocazioni]]
[[Categoria:Ottimizzare C++|Allocazioni e deallocazioni]]
{{Avanzamento|75%|23 maggio 2008}}
{{Avanzamento|100%|25 maggio 2008}}

Versione delle 10:50, 25 mag 2008

Indice del libro

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.

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 veniva fatta in linguaggio C con le funzioni malloc e free. 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

Se un array statico o non grande ha lunghezza costante, non usare un oggetto vector, ma 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 è medio o grande, tali vantaggi diminuiscono, e invece risulta più importante evitare di usare troppo spazio sullo stack.

Allocatore a blocchi

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, 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 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

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 del 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.