2012-05-08 12 views
9

Sto scrivendo un'applicazione performance critical in cui sto creando un numero elevato di oggetti di tipo simile per effettuare ordini. Sto usando boost::singleton_pool per allocare memoria. Finalmente la mia classe sembra così.Gestione delle variabili membro std :: string/std :: vector durante l'utilizzo di boost :: singleton_pool

class MyOrder{ 
    std::vector<int> v1_; 
    std::vector<double> v2_; 

    std::string s1_; 
    std::string s2_; 

public: 
    MyOrder(const std::string &s1, const std::string &s2): s1_(s1), s2_(s2) {} 

    ~MyOrder(){} 

    static void * operator new(size_t size); 
    static void operator delete(void * rawMemory) throw(); 
    static void operator delete(void * rawMemory, std::size_t size) throw(); 

}; 

struct MyOrderTag{}; 
typedef boost::singleton_pool<MyOrderTag, sizeof(MyOrder)> MyOrderPool; 

void* MyOrder:: operator new(size_t size) 
{ 
    if (size != sizeof(MyOrder)) 
     return ::operator new(size); 

    while(true){ 
     void * ptr = MyOrderPool::malloc(); 
     if (ptr != NULL) return ptr; 

     std::new_handler globalNewHandler = std::set_new_handler(0); 
     std::set_new_handler(globalNewHandler); 

     if(globalNewHandler) globalNewHandler(); 
     else throw std::bad_alloc(); 

    } 
} 

void MyOrder::operator delete(void * rawMemory) throw() 
{ 
    if(rawMemory == 0) return; 
    MyOrderPool::free(rawMemory); 
} 

void MyOrder::operator delete(void * rawMemory, std::size_t size) throw() 
{ 
    if(rawMemory == 0) return; 
    if(size != sizeof(Order)) { 
     ::operator delete(rawMemory); 
    } 
    MyOrderPool::free(rawMemory); 
} 

Recentemente ho inviato un question su miglioramento delle prestazioni nell'utilizzo boost :: singleton_pool. Quando ho confrontato le prestazioni di boost::singleton_pool e l'allocatore predefinito, non ho ottenuto alcun vantaggio in termini di prestazioni. Quando qualcuno ha indicato che la mia classe aveva membri del tipo std :: string, la cui allocazione non era governata dal mio allocatore personalizzato, ho rimosso le variabili std :: string e reran i test. Questa volta ho notato un notevole aumento delle prestazioni.

  1. Ora, nella mia applicazione effettiva, non posso liberarmi di variabili membro di tempo std :: string e std :: vector. Dovrei usare boost::pool_allocator con le mie variabili membro std :: string e std :: vector?

  2. boost :: pool_allocator alloca la memoria da uno std :: singleton_pool sottostante. Importerà se diverse variabili membro (ho più di un tipo std :: string/std :: vector nella mia classe MyOrder. Inoltre sto utilizzando pool per classi diverse da MyOrder che contengono i tipi std :: string/std :: vector come membri) utilizzare lo stesso pool di memoria? Se lo fa, come faccio ad assicurarmi che facciano in un modo o nell'altro?

+1

Usa const e nel tuo costruttore. –

+1

@EternalLearner Good Point. Ciò impedirà una copia. Fatto. – sank

+0

non sarebbe un po 'più appropriato usare effettivamente std :: weak_ptr's o qualcosa per lo std :: stringhe qui? prendere riferimenti per copie interne private mi sembra sempre sbagliato. anche se rinvii la copia ... –

risposta

2
  1. Ora, nella mia domanda effettiva, che non può sbarazzarsi di variabili membro di tempo std :: string e std :: vector. Dovrei usare boost :: pool_allocator con le mie variabili membro std :: string e std :: vector?

non ho mai guardato in quella parte di spinta, ma se si desidera modificare in cui le stringhe allocano la loro memoria, è necessario passare un allocatore diverso per std::basic_string<> in fase di compilazione. Non c'è altro modo. Tuttavia, è necessario essere consapevoli dei lati negativi di questo: Ad esempio, tali stringhe non saranno più assegnabili a std::string. (Anche se impiegando c_str() funzionerebbe, potrebbe imporre una piccola riduzione delle prestazioni.)

  1. boost :: pool_allocator alloca la memoria da un sottostante std :: singleton_pool. Importerà se diverse variabili membro (ho più di un tipo std :: string/std :: vector nella mia classe MyOrder. Inoltre sto utilizzando pool per classi diverse da MyOrder che contengono i tipi std :: string/std :: vector come membri) utilizzare lo stesso pool di memoria? Se lo fa, come faccio ad assicurarmi che facciano in un modo o nell'altro?

L'intero punto di una piscina è inserire più di un oggetto. Se fosse solo uno, non avresti bisogno di una piscina. Quindi, sì, puoi inserire diversi oggetti, compresa la memoria dinamica di diversi oggetti std::string.

Se si ottengono guadagni in termini di prestazioni, tuttavia, resta da vedere. Si utilizza un pool perché si ha motivo di supporre che sia più veloce dell'allocazione generale (anziché utilizzarlo per, ad esempio, allocare memoria da un'area specifica, ad esempio la memoria condivisa). Solitamente un pool di questo tipo è più veloce perché può fare supposizioni sulle dimensioni degli oggetti allocati all'interno.Questo è certamente vero per la tua classe MyOrder: gli oggetti hanno sempre la stessa dimensione, altrimenti (classi derivate più grandi) non li allocerai nel pool.
Questo è diverso per std::string. L'intero punto di utilizzo di una classe di stringhe che assegna dinamicamente è che si adatta a qualsiasi lunghezza di stringa. I pezzi di memoria necessari per questo sono di dimensioni diverse (altrimenti potreste semplicemente utilizzare i char array). Vedo poco spazio per un allocatore di piscine per migliorare l'allocatore generale per quello.


Su un lato nota: Il tuo sovraccarico operator new() restituisce il risultato di invocare quella globale, ma il vostro operator delete appena passa nulla venire il suo modo di quel pool di free(). Mi sembra molto sospetto.

+0

Nella mia domanda ho approfondito più "variabili diverse". – sank

+0

Sto seguendo esattamente la procedura descritta in Effective C++ (terza edizione) articoli 49 e 51 (http://my.safaribooksonline.com/0321334876) – sank

+1

@sank: non ho ancora trovato il tempo di procurarmi una copia del libro e guardarlo, ma ho sparato un link a questo verso Scott Meyers, e mi ha scritto che (sto ri-traducendo questo dal tedesco) "ci sono due versioni di" operator delete "a pagina 255. La prima è la versione non membro, ed è quella che ha copiato.La seconda versione è la versione del membro ed è quella che avrebbe dovuto copiare.Contiene il test che si suppone si aspettasse: [...] 'if (size! = sizeof (Base)) '[...]". Quindi potresti voler tornare indietro e leggerlo più a fondo. HTH! – sbi

1

L'utilizzo di un allocatore personalizzato per il std::string/std::vector nella classe funzionerebbe (presupponendo che l'allocatore sia corretto) - ma solo il test delle prestazioni vedrà se si vedono davvero dei benefici da esso.

In alternativa, se si sa che il std::string/std::vector avrà limiti superiori, si potrebbe implementare un sottile involucro attorno a un std::array (array o normale se non si dispone di C++ 11), che lo rende un rimpiazzo .

Anche se la dimensione è illimitato, se c'è qualche dimensione che maggior parte dei valori sarebbe inferiore, si potrebbe estendere le implementazioni basate std::array sopra per essere espandibile assegnando con il tuo allocatore pool se si riempiono.

Problemi correlati