2014-10-24 14 views
7

więc zrobiłem tego kontenera przydzielania memory_pools klasa oparte na basenie doładowania:doładowania basen podzielnik wolniej niż nowe

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 

potem, kiedy go przetestować, używając:

#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); 
} 

Z clang3.5 na MacOSX 10.10, mam:

$ time ./a.out 

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

Zważywszy, kiedy uruchomić:

#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); 
} 

mam :

$ time ./a.out 

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

Pytanie

Czy alokacja pamięci przy użyciu basen doładowania miało być, że powolne lub jest moja próba nieważne z jakiegoś powodu?


EDIT

Po Carmeron „s komentarza, dodałem -O3 i -DNDEBUG flagi, teraz mam:

$time ./a.out 

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

dla wersji memory_pools oraz:

$ time ./a.out 

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

dla standardowej wersji programu przydzielania.

Pytanie

Pytanie nadal posiada, jest to normalne, to jest wolniejsze?

+0

Czy sprawdziłeś kod zespołu, aby upewnić się, że pętla testowa nie jest zoptymalizowana? Wszystko to z '-O3 -DNDEBUG', prawda? – Cameron

+0

@Cameron thx za cynk, zredagowałem pytanie :) – Drax

+0

W zależności od implementacji STL możesz stracić dużo czasu w kodzie reindeksowym. –

Odpowiedz

6

Nigdy nie użyłem kodu Pool Boost, a nawet go odczytałem. Ale wiem ogólnie o pulach pamięci i nie oczekuję, że pula pamięci w twoim teście przewyższy wydajność malloc.

Aby to zrozumieć, musisz najpierw zrozumieć, w jaki sposób zaimplementowano malloc i free, jeśli jeszcze tego nie zrobiłeś. Odpowiedzi na to pytanie wydają się być dość dobrym podsumowaniem: How do malloc() and free() work?

Fragmentacja pamięci jest trudnym problemem dla malloc() i free(), a nie ma prostego, szybkiego rozwiązania. Ale jest o wiele łatwiej, jeśli możesz zagwarantować, że wszystkie alokacje są tej samej wielkości: w ten sposób można wygrać puli pamięci.Ale twój test nie wymaga dużej fragmentacji pamięci i prawdopodobnie wcale nie zwalnia dużej ilości pamięci. Więc w tym teście wygrywa malloc(), a pule przegrywają. Aby zawęzić testu, można mieszać w pęczek usuwa coś takiego:

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

Powiedziawszy to wszystko, jeśli naprawdę chcą wiedzieć, dlaczego dany fragment kodu wykonuje sposób to robi, ty powinien go profilować. Warto wymyślić teorie o tym, dlaczego fragment kodu zachowuje się w określony sposób, ale musisz także przetestować swoje teorie.

+0

Chciałbym spróbować losowo przydzielać i rozdzielać rzeczy o losowych rozmiarach, tworząc listę alokacji i losowo alokując lub, jeśli lista nie jest pusta, wycofując losowy element z listy. Gdy zaczniesz otrzymywać alokacje, zaczniesz dzielić pamięć. –

Powiązane problemy