2012-12-03 8 views
6

Recentemente mi sono divertito a sviluppare un allocatore personalizzato basato su un pool di memoria, condiviso tra più istanze dell'allocatore.Contenitori STL, conflitti SBO e allocatori personalizzati

L'intenzione era che l'allocatore essere compatibile con STL e Standard C++ basate contenitori, come vettore, deque, carta, corda, ecc

Tuttavia qualcosa in particolare mi ha causato una certa confusione. Varie implementazioni dei contenitori come std :: vector, std :: string fanno uso di Small Buffer Optimization - allocazione basata su stack per piccoli requisiti di memoria iniziale.

Ad esempio MSVC9.1 ha il seguente membro della classe basic_string:

union _Bxty 
{ // storage for small buffer or pointer to larger one 
    _Elem _Buf[_BUF_SIZE]; 
    _Elem *_Ptr; 
    char _Alias[_BUF_SIZE]; // to permit aliasing 
} _Bx; 

non si vede come quando si istanzia tali contenitori si può blandire all'attuazione al solo ed utilizzare sempre l'allocatore disponibile e non usare SBO. Chiedo perché una delle intenzioni di implementare gli allocatori personalizzati era quella di poterli utilizzare in un contesto di memoria condivisa , dove la quantità di memoria condivisa può essere inferiore a il limite SBO che alcune delle varie implementazioni potrebbero utilizzare.

Per esempio mi piacerebbe avere una situazione in cui posso avere due istanze di std :: string uno per ogni processo di condivisione di un blocco comune di memoria che forse minore o uguale al SBO superiore limite.

possibilmente correlate: May std::vector make use of small buffer optimization?

typedef std::vector<int,mysharedmemallocator> shmvtype; 

shmvtype v(2,0); //<-- if SBO then error as memory is allocated on 
       //stack not via the allocator 

v[1] = 1234; //<-- if SBO then error as wrong piece of memory 
       // is being modified. 

Vediamo un altro esempio che non si basa sulla memoria condivisa come sembra a più di complicare le cose per alcune persone. Diciamo che voglio specializzare il mio std :: basic_string o std :: vector etc con un allocatore che riempie la memoria allocata con il valore 0xAB prima di presentare il puntatore all'entità chiamante per nessun'altra ragione se non un capriccio.

Un contenitore specializzato con questo nuovo allocatore, ma che utilizza anche SBO, non avrà la sua memoria basata su SBO riempita con il modello 0xAB. Così, per esempio:

typedef std::basic_string<char,myfillmemallocator> stype 

stype s; 
s.resize(2); 

assert(s[0] == 0xAB); // if SBO this will fail. 

risposta

4

una delle intenzioni di attuare allocatori usanza era quella di essere in grado di utilizzarli in un contesto di memoria condivisa

Questo può essere ciò che si intende fare con esso, ma non è per questo che esistono. Infatti, con l'eccezione di basic_string in C++ 98/03, non è legale per condividere la memoria allocata tra gli oggetti. Possono condividere oggetti allocator, in modo che possano ottenere la loro memoria dallo stesso posto. Ma è illegale che le modifiche di un oggetto influiscano su un altro che non è correlato; ogni istanza deve essere separata.

Le stringhe copy-on-write funzionano solo perché il sistema presuppone che qualsiasi accesso non costante a un carattere possa scrivere su di esso, eseguendo così una copia. E in C++ 11, anche basic_string è vietato fare cose di tipo copy-on-write come questo.

Ad esempio, mi piacerebbe avere una situazione in cui posso avere due istanze di std :: string one per processo che condividono un blocco di memoria comune che può essere inferiore o uguale al limite superiore SBO.

Questo non è possibile senza scrivere la tua classe. L'allocatore controlla solo da dove proviene la memoria. Quello che vuoi è una stringa copy-on-write garantita o una sorta di classe di stringa condivisa.

Quello che vuoi richiede una classe contenitore appositamente progettata per questo scopo.

+1

Grazie per il commento Nicol, ma solo una domanda, non si può avere un allocatore che alloca la memoria condivisa e alias la memoria tramite un concetto di puntatore di offset - Questo è come avviene normalmente (boost.interprocess), non di sicuro capisco perché non può essere fatto. –

+2

@Seminario: E cosa succede se copi quella stringa? Cosa succede se si inserisce un carattere in quella stringa, causando potenzialmente una riallocazione? Non è chiaro quale dovrebbe essere la semantica di una di queste operazioni, ma una cosa è chiara: qualunque sia la semantica, sono semantiche di cui l'implementazione stessa deve essere a conoscenza. Cioè, hai bisogno di una specifica implementazione del contenitore che faccia le cose in un certo modo. –

+1

Per quanto riguarda la copia dalla stringa condivisa ad un'altra stringa è una copia comune, non ci si aspetta che la stringa di destinazione abbia memoria condivisa, questo è anche lo stesso al contrario, copia da normale std :: string a dati di stringa condivisi copiati nella memoria condivisa fornita dall'allocatore specializzato - Quindi questo è irrilevante per la domanda –