2014-10-24 14 views
7

Così ho fatto questo allocatore contenitore memory_pools classe basata sulla piscina spinta:Boost piscina allocatore più lento di nuovo

memory_pools.hpp

#ifndef MEMORY_POOL_HPP 
# define MEMORY_POOLS_HPP 

// boost 
# include <boost/pool/pool.hpp> 
# include <boost/unordered_map.hpp> 

template<typename ElementType> 
class memory_pools 
{ 
public: 
    template <typename> 
    friend class memory_pools; 

private: 
    using pool = boost::pool<>; 

public: 
    using value_type = ElementType; 
    using pointer = value_type*; 
    using const_pointer = const value_type*; 
    using reference = value_type&; 
    using const_reference = const value_type&; 
    using size_type = pool::size_type; 
    using difference_type = pool::difference_type; 

public: 

    template<typename OtherElementType> 
    struct rebind 
    { 
    using other = memory_pools<OtherElementType>; 
    }; 

public: 
    memory_pools(); 

    template<typename SourceElement> 
    memory_pools(const memory_pools<SourceElement>&); 

public: 
    pointer allocate(const size_type n); 
    void deallocate(const pointer ptr, const size_type n); 

    template<typename... Args> 
    void construct(pointer, Args...); 
    void destroy(pointer); 

public: 
    bool operator==(const memory_pools&); 
    bool operator!=(const memory_pools&); 

private: 
    using pools_map = boost::unordered_map<std::size_t, std::shared_ptr<pool>>; 

private: 
    std::shared_ptr<pools_map>  pools_map_; 
    std::shared_ptr<pool>   pool_; 
}; 

# include <memory_pools.ipp> 

#endif 

memory_pools.ipp

#ifndef MEMORY_POOLS_IPP 
# define MEMORY_POOLS_IPP 

template<typename ElementType> 
memory_pools<ElementType>::memory_pools() 
    : 
    pools_map_(std::make_shared<pools_map> 
      (pools_map 
      { 
       std::make_pair 
       (sizeof(ElementType), 
        make_shared<pool>(sizeof(ElementType))) 
      })), 
    pool_(pools_map_->at(sizeof(ElementType))) 
{ 
} 

template<typename ElementType> 
template<typename SourceElement> 
memory_pools<ElementType>::memory_pools 
(const memory_pools<SourceElement>& rebinded_from) 
    : 
    pools_map_(rebinded_from.pools_map_), 
    pool_(pools_map_->insert 
     (std::make_pair(sizeof(ElementType), 
         make_shared<pool>(sizeof(ElementType)))).first->second) 
    { 
    } 

template<typename ElementType> 
typename memory_pools<ElementType>::pointer memory_pools<ElementType>::allocate 
(const size_type n) 
{ 
    pointer ret = static_cast<pointer>(pool_->ordered_malloc(n)); 

    if ((!ret) && n) 
    throw std::bad_alloc(); 

    return (ret); 
} 

template<typename ElementType> 
void  memory_pools<ElementType>::deallocate 
(const pointer ptr, const size_type n) 
{ 
    pool_->ordered_free(ptr, n); 
} 

template<typename ElementType> 
template<typename... Args> 
void  memory_pools<ElementType>::construct(pointer ptr, Args... args) 
{ 
    new (ptr) ElementType(std::forward<Args>(args)...); 
} 

template<typename ElementType> 
void  memory_pools<ElementType>::destroy(pointer ptr) 
{ 
    ptr->~ElementType(); 
} 

template<typename ElementType> 
bool  memory_pools<ElementType>::operator==(const memory_pools& rhs) 
{ 
    return (pools_map_ == rhs.pools_map_); 
} 

template<typename ElementType> 
bool  memory_pools<ElementType>::operator!=(const memory_pools& rhs) 
{ 
    return (pools_map_ != rhs.pools_map_); 
} 

#endif 

Poi quando i test utilizzando:

#include <memory_pools.hpp> 

int  main(void) 
{ 
    using pools_type = memory_pools<std::pair<const int, int>>; 
    pools_type pools; 

    boost::unordered_map<int, int, boost::hash<int>, std::equal_to<int>, pools_type>  map; 
    //boost::unordered_map<int, int, boost::hash<int>, std::equal_to<int>>  map; 

    for (unsigned int i = 0; i < 20000; ++i) 
    { 
     map[i] = i + 1; 
    } 

    return (0); 
} 

Con clang3.5 su MacOSX 10.10, ho ottenuto:

$ time ./a.out 

real 0m1.873s 
user 0m1.850s 
sys  0m0.009s 

Mentre quando lancio:

#include <memory_pools.hpp> 

int  main(void) 
{ 
    using pools_type = memory_pools<std::pair<const int, int>>; 
    pools_type pools; 

    //boost::unordered_map<int, int, boost::hash<int>, std::equal_to<int>, pools_type>  map; 
    boost::unordered_map<int, int, boost::hash<int>, std::equal_to<int>>  map; 

    for (unsigned int i = 0; i < 20000; ++i) 
    { 
     map[i] = i + 1; 
    } 

    return (0); 
} 

ho :

$ time ./a.out 

real 0m0.019s 
user 0m0.016s 
sys  0m0.002s 

Domanda

è l'allocazione di memoria utilizzando piscina spinta dovrebbe essere lento o che è la mia prova valida per qualche motivo?


EDIT

Dopo Carmeron 's commento, ho aggiunto il -O3 e -DNDEBUG bandiere, ora ho:

$time ./a.out 

real 0m0.438s 
user 0m0.431s 
sys  0m0.003s 

per la versione memory_pools, e:

$ time ./a.out 

real 0m0.008s 
user 0m0.006s 
sys  0m0.002s 

per la versione di allocatore standard.

Domanda

La domanda detiene ancora, è normale è più lento?

+0

Avete controllato il codice assembly per garantire che il vostro ciclo di test non venga ottimizzato? Tutto questo è con '-O3 -DNDEBUG', giusto? – Cameron

+0

@ Cameron thx per il suggerimento, ho modificato la domanda :) – Drax

+0

A seconda dell'implementazione STL si potrebbe perdere molto tempo nel codice rebind. –

risposta

6

Non ho mai usato il codice del pool di Boost, o nemmeno letto. Ma conosco alcune cose sui pool di memoria in generale e non mi aspetto che il pool di memoria nel test superi la malloc.

Per capire questo, è necessario prima capire come malloc e libero vengono implementati se non lo si è già fatto. Le risposte a questa domanda sembrano fornire una sintesi abbastanza buona: How do malloc() and free() work?

La frammentazione della memoria è un problema difficile per malloc() e free() e non esiste una soluzione semplice e veloce. Ma è molto, molto più semplice se puoi garantire che tutte le tue allocazioni abbiano le stesse dimensioni: è così che possono vincere i pool di memoria.Ma il tuo test non comporta molta frammentazione della memoria e probabilmente non libera affatto molta memoria. Quindi in questo test vince malloc() e i pool perdono. Per raffinare la prova, si potrebbe mescolare in un mucchio di eliminazioni, qualcosa come:

// Allocate 10,000 things 
// Loop 10 times: 
// Allocate 1,000 things 
// Delete 1,000 things 

Detto questo, se si davvero vuoi sapere perché un particolare pezzo di codice esegue il modo in cui lo fa, si dovrebbe profilare È utile pensare a teorie sul perché un codice si comporta in un modo particolare, ma devi anche testare le tue teorie.

+0

Proverei a allocare e deallocare in modo casuale le cose di dimensioni casuali creando un elenco di allocazioni e assegnando casualmente o, se la lista non è vuota, rilasciare un elemento casuale della lista. Una volta che inizierai a fare delle allocazioni, inizierai a frammentare la tua memoria. –

Problemi correlati