8

だから私は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);
}

macOSX 10.10 で clang3.5 を使用すると、次のようになりました。

$ 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

標準のアロケータ バージョンの場合。

質問

質問はまだありますが、遅いのは正常ですか?

4

1 に答える 1

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

とはいえ、特定のコードがそのように動作する理由を本当に知りたい場合は、それをプロファイリングする必要があります。コードの一部が特定の方法で動作する理由について理論を考えることは役に立ちますが、その理論をテストする必要もあります。

于 2014-10-24T18:37:18.743 に答える