Allocatori di heap personalizzati


9

La maggior parte dei programmi può essere abbastanza informale sull'allocazione dell'heap, anche nella misura in cui i linguaggi di programmazione funzionale preferiscono allocare nuovi oggetti piuttosto che modificare quelli vecchi e lasciare che il garbage collector si preoccupi di liberare le cose.

Nella programmazione integrata, il settore silenzioso, tuttavia, ci sono molte applicazioni in cui non è possibile utilizzare l'allocazione dell'heap, a causa della memoria e dei vincoli in tempo reale; il numero di oggetti di ciascun tipo che verrà gestito fa parte delle specifiche e tutto viene allocato staticamente.

La programmazione dei giochi (almeno con quei giochi che sono ambiziosi nel spingere l'hardware) a volte si frappone: puoi usare l'allocazione dinamica, ma ci sono memoria sufficiente e vincoli soft in tempo reale che non puoi considerare l'allocatore come una scatola nera , figuriamoci usare la garbage collection, quindi devi usare allocatori personalizzati. Questo è uno dei motivi per cui il C ++ è ancora ampiamente usato nell'industria dei giochi; ti permette di fare cose come http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2271.html

Quali altri domini si trovano in quel territorio intermedio? Dove, a parte i giochi, vengono utilizzati pesantemente gli allocatori personalizzati?


1
Alcuni sistemi operativi utilizzano un allocatore di lastre che fornisce la memorizzazione degli oggetti nella cache ma può anche essere utilizzato per ridurre i conflitti di errore nella cache del processore mappando i membri di un oggetto su set diversi per una cache indicizzata modulo 2 ** N (entrambi con più istanze in una memoria contigua e mediante imbottitura variabile all'interno della soletta). Il comportamento della cache può essere più importante dell'allocazione / della velocità libera o dell'utilizzo della memoria in alcuni casi.
Paul A. Clayton,

Risposte:


4

Ogni volta che si dispone di un'applicazione che ha un percorso critico ad alte prestazioni, è necessario considerare il modo in cui si tratta la memoria. La maggior parte delle applicazioni lato client dell'utente finale non rientra in questa categoria perché sono primarie basate sugli eventi e la maggior parte degli eventi proviene da interazioni con l'utente e che non ha molti (se non tutti) vincoli di prestazione.

Tuttavia, molti software di back-end dovrebbero concentrarsi su come viene gestita la memoria perché molti di questi software possono scalare per gestire un numero maggiore di client, un numero maggiore di transazioni, più origini dati .... Una volta avviato spingendo i limiti, puoi iniziare ad analizzare come la memoria degli utenti del tuo software e scrivere schemi di allocazione personalizzati su misura per il tuo software piuttosto che fare affidamento su un allocatore di memoria completamente generico che è stato scritto per gestire qualsiasi caso d'uso immaginabile.

Per darvi alcuni esempi ... nella mia prima azienda ho lavorato su un pacchetto Historian, software responsabile della raccolta / archiviazione / archiviazione dei dati di controllo di processo (pensate a una fabbrica, una centrale nucleare o una raffineria di petrolio con 10 milioni di sensori, archiviamo tali dati). Ogni volta che analizzavamo eventuali colli di bottiglia delle prestazioni che impedivano allo storico di elaborare più dati, la maggior parte delle volte il problema riguardava il modo in cui veniva gestita la memoria. Abbiamo fatto di tutto per assicurarci che malloc / free non fossero chiamati a meno che non fossero assolutamente necessari.

Nel mio attuale lavoro, lavoro sul registratore digitale di videosorveglianza e sul pacchetto di analisi. A 30 fps, ogni canale riceve un fotogramma video ogni 33 millisecondi. Sull'hardware che vendiamo, possiamo facilmente registrare 100 canali di video. Questo è un altro caso per assicurarsi che nel percorso critico (chiamata di rete => componenti di acquisizione => software di gestione del registratore => componenti di archiviazione => disco) non vi siano allocazioni di memoria dinamica. Abbiamo un allocatore di frame personalizzato, che contiene bucket di buffer di dimensioni fisse e utilizza LIFO per riutilizzare i buffer allocati in precedenza. Se hai bisogno di 600 Kb di spazio di archiviazione, potresti finire con un buffer di 1024 Kb, che spreca spazio, ma poiché è su misura specificamente per il nostro uso in cui ogni allocazione ha una durata molto breve, funziona molto bene perché viene utilizzato il buffer,

Nel tipo di applicazioni che ho descritto (spostare molti dati da A a B e gestire un gran numero di richieste client) andare all'heap e viceversa è una delle principali fonti di colli di bottiglia nelle prestazioni della CPU. Mantenere la frammentazione dell'heap al minimo è un vantaggio secondario, tuttavia per quanto ne so la maggior parte dei sistemi operativi moderni implementa già heap a bassa frammentazione (come minimo so che Windows lo fa, e spero che lo facciano anche altri). Personalmente, in oltre 12 anni di lavoro in questi tipi di ambienti, ho visto problemi di utilizzo della CPU legati all'heap abbastanza frequentemente, mentre mai una volta ho visto un sistema che soffriva di un heap frammentato.


"Abbiamo fatto di tutto per assicurarci che malloc / free non fossero chiamati a meno che non fossero assolutamente necessari ..." - Conosco alcuni ragazzi hardware che costruiscono router. Non si preoccupano nemmeno malloc/free. Riservano un blocco di memoria e lo usano come struttura di dati del cursore. Gran parte del loro lavoro si è ridotto per tenere traccia degli indici.

4

Elaborazione video, VFX, sistemi operativi, ecc. Spesso però le persone li abusano. La struttura dei dati e l'allocatore non devono essere separati per ottenere un'allocazione efficiente.

Ad esempio, sta introducendo molta ulteriore complessità per dividere l'allocazione efficiente dei nodi degli alberi in un ottetto di distanza dall'ottico stesso e fare affidamento su un allocatore esterno. Non è necessariamente una violazione di SRP fondere insieme queste due preoccupazioni e rendere la responsabilità dell'octree di allocare più nodi contemporaneamente in modo contiguo, in quanto ciò non aumenta il numero di motivi per cambiare. In pratica, può ridurlo.

Nel C ++, ad esempio, uno degli effetti collaterali ritardati di avere container standard si basano su un allocatore esterno ha reso le strutture collegate simili std::mape std::listconsiderate quasi inutili dalla comunità C ++, dal momento che le stanno confrontandostd::allocatormentre queste strutture di dati assegnano un nodo alla volta. Ovviamente le tue strutture collegate funzioneranno male in quel caso, ma le cose sarebbero andate così diversamente se l'allocazione efficiente dei nodi per le strutture collegate fosse considerata una responsabilità di una struttura di dati piuttosto che di un allocatore. Potrebbero comunque utilizzare un'allocazione personalizzata per altri motivi come il tracciamento / profiling della memoria, ma fare affidamento sull'allocatore per rendere efficienti le strutture collegate durante il tentativo di allocare nodi una alla volta rende tutte, per impostazione predefinita, estremamente inefficienti, il che andrebbe bene se venisse con un noto avvertimento che le strutture collegate ora hanno bisogno di un allocatore personalizzato, come la lista libera, per essere ragionevolmente efficiente ed evitare di innescare mancate cache a destra e sinistra. Molto più praticamente applicabile potrebbe essere stato qualcosa di similestd::list<T, BlockSize, Alloc>, dove BlockSizeindica il numero di nodi contigui da allocare contemporaneamente per l'elenco libero (specificare 1 porterebbe effettivamente a std::listcom'è ora).

Ma non esiste tale avvertimento, il che porta quindi a un'intera comunità di teste di legno che fanno eco a un mantra di culto che elenchi collegati sono inutili, ad es.


3

Un'altra area in cui potresti desiderare un allocatore personalizzato è quella di prevenire la frammentazione dell'heap . Nel tempo, l'heap potrebbe allocare piccoli oggetti frammentati in tutto l'heap. Se il tuo programma non riesce a tenere insieme la memoria dell'heap, quando il programma va ad allocare un oggetto più grande, deve rivendicare più memoria dal sistema in quanto non riesce a trovare un blocco libero tra l'heap frammentato esistente (troppi piccoli gli oggetti sono in mezzo). L'utilizzo totale della memoria del programma aumenterà nel tempo e consumerai ulteriori pagine di memoria inutilmente. Quindi questo è un grosso problema per i programmi che dovrebbero funzionare per lunghi periodi di tempo (pensate a database, server, ecc. Ecc.).

Dove, a parte i giochi, vengono utilizzati pesantemente gli allocatori personalizzati?

Facebook

Scopri jemalloc che Facebook sta iniziando a utilizzare per migliorare le prestazioni dell'heap e ridurre la frammentazione.


Giusto. Tuttavia, un garbage collector che copia risolve ordinatamente il problema della frammentazione, non è vero?
rwallace,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.