Skip to content

Instantly share code, notes, and snippets.

@arrieta
Created November 3, 2018 18:21
Show Gist options
  • Save arrieta/42ee41713b9a54fdea7fc7be9335ada1 to your computer and use it in GitHub Desktop.
Save arrieta/42ee41713b9a54fdea7fc7be9335ada1 to your computer and use it in GitHub Desktop.
Aligned Object Pool
#include <cassert>
#include <iostream>
#include <memory>
#include <type_traits>
#include <vector>
template <typename T>
class Pool {
public:
using Block = typename std::aligned_storage<sizeof(T), alignof(T)>::type;
using Page = std::unique_ptr<Block[]>;
using Pages = std::vector<Page>;
constexpr std::size_t blocks_per_page() const {
return PAGE_SIZE / sizeof(Block);
}
constexpr std::size_t size() const { return m_size; }
std::size_t capacity() const { return m_pages.size() * blocks_per_page(); }
template <typename... Args>
void emplace(Args&&... args) {
if (size() == capacity()) {
m_pages.emplace_back(std::make_unique<Block[]>(blocks_per_page()));
}
auto page = m_pages.back().get();
auto offset = sizeof(Block) * ((m_size++) % blocks_per_page());
new (std::next(page, offset)) T(std::forward<Args>(args)...);
}
const T& at(std::size_t n) const {
auto page = m_pages.at(n / blocks_per_page()).get();
auto offset = sizeof(Block) * (n % blocks_per_page());
return *reinterpret_cast<T*>(std::next(page, offset));
}
T& at(std::size_t n) {
auto page = m_pages.at(n / blocks_per_page()).get();
auto offset = sizeof(Block) * (n % blocks_per_page());
return *reinterpret_cast<T*>(std::next(page, offset));
}
private:
std::size_t m_size{0};
Pages m_pages{};
};
struct alignas(16) Vector {
Vector() {}
Vector(double x, double y, double z) : x{x}, y{y}, z{z} {}
double x{0};
double y{0};
double z{0};
};
std::ostream& operator<<(std::ostream& os, const Vector& v) {
return os << v.x << ", " << v.y << ", " << v.z;
}
int main() {
Pool<Vector> pool;
pool.emplace();
pool.emplace(1, 2, 3);
pool.emplace(4, 5, 6);
std::cout << pool.size() << "\n" << pool.capacity() << "\n";
std::cout << pool.at(0) << "\n" << pool.at(1) << "\n" << pool.at(2) << "\n";
pool.at(2).x += 10;
std::cout << pool.at(2) << "\n";
}
@arrieta
Copy link
Author

arrieta commented Nov 3, 2018

Type erasure

#include <cassert>
#include <iostream>
#include <memory>
#include <type_traits>
#include <vector>

static constexpr const std::size_t PAGE_SIZE = 8192;

class PoolBase {
 public:
  PoolBase(std::size_t item_size, std::size_t page_size) noexcept
      : m_item_size{item_size},
        m_page_size{page_size},
        m_items_per_page{m_page_size / m_item_size} {}

  std::size_t size() const noexcept { return m_size; }

  std::size_t capacity() const noexcept {
    return m_pages.size() * m_items_per_page;
  }

  const void* address(std::size_t n) const {
    assert(n < size());
    auto page   = n / m_items_per_page;
    auto offset = n - m_items_per_page * page;
    auto ptr    = std::next(m_pages[page].get(), offset);
    return static_cast<void*>(ptr);
  }

  void* address(std::size_t n) {
    assert(n < size());
    auto page   = n / m_items_per_page;
    auto offset = n - m_items_per_page * page;
    auto ptr    = std::next(m_pages[page].get(), offset);
    return static_cast<void*>(ptr);
  }

  void reserve_page() {
    m_pages.emplace_back(std::make_unique<char[]>(m_page_size));
  }

  void destroy(std::size_t n) { return this->do_destroy(n); }

 protected:
  ~PoolBase() = default;

 private:
  virtual void do_destroy(std::size_t n) = 0;

  std::size_t                          m_item_size{0};
  std::size_t                          m_page_size{0};
  std::size_t                          m_items_per_page{0};
  std::size_t                          m_size{0};
  std::vector<std::unique_ptr<char[]>> m_pages{};
};

template <typename T>
class Pool : public PoolBase {
 public:
  Pool() : PoolBase(sizeof(T), PAGE_SIZE) {}

 private:
  void do_destroy(std::size_t n) override {
    auto ptr = static_cast<T*>(address(n));
    ptr->~T();
  }
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment