Распределитель пула Boost медленнее, чем новый

Так что я сделал этот распределитель контейнеров memory_pools класс на основе буст-пула:

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

Затем, когда я проверяю это с помощью:

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

С Clang3.5 на MacOSX 10.10 я получил:

$ time ./a.out

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

Принимая во внимание, когда я запускаю:

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

Я имею:

$ time ./a.out

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

Вопрос

Предполагается, что выделение памяти с использованием пула бустов будет таким медленным или по какой-то причине мой тест будет неверным?


РЕДАКТИРОВАТЬ

После Carmeronкомментарий, я добавил -O3 а также -DNDEBUG флаги, теперь у меня есть:

$time ./a.out

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

для memory_pools версия и:

$ time ./a.out

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

для стандартной версии распределителя.

Вопрос

Вопрос остается в силе, нормально ли это медленнее?

7

Решение

Я никогда не использовал код пула Boost и даже не читал его. Но я знаю кое-что о пулах памяти в целом, и я не ожидал бы, что пул памяти в вашем тесте превзойдет malloc.

Чтобы понять это, вы должны сначала понять, как реализованы malloc и free, если вы этого еще не сделали. Ответы на этот вопрос, кажется, дают довольно хорошее резюме: Как работают malloc () и free ()?

Фрагментация памяти является сложной проблемой для malloc() а также free()и нет простого, быстрого решения. Но это намного, намного проще, если вы можете гарантировать, что все ваши выделения имеют одинаковый размер: именно так могут выиграть пулы памяти. Но ваш тест не требует большой фрагментации памяти и, вероятно, вообще не освобождает много памяти. Итак, в этом тесте malloc() выигрывает, а пулы проигрывают. Чтобы улучшить ваш тест, вы можете смешать в кучу удалений, что-то вроде:

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

Сказав все это, если вы действительно Если вы хотите знать, почему определенный фрагмент кода работает так, как он есть, вы должны его профилировать. Полезно придумать теории о том, почему фрагмент кода ведет себя определенным образом, но вы также должны проверить свои теории.

6

Другие решения


По вопросам рекламы [email protected]