Skip to content

Commit

Permalink
Dynamic block size for custom allocator (API change):
Browse files Browse the repository at this point in the history
This changes the internal arena allocator to dynamically adjust
the block size based on the allocation throughput. It removes
the requirement that callers specify the alloc size.

Public interfaces that previously required an allocation size
hint, now no longer require the hint. It is sufficient to simply
delete the parameter from call sites.
  • Loading branch information
vinniefalco committed Sep 6, 2016
1 parent 506980e commit 517510a
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 51 deletions.
8 changes: 1 addition & 7 deletions include/nudb/basic_store.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,6 @@ class basic_store
using clock_type =
std::chrono::steady_clock;

using shared_lock_type =
boost::shared_lock<boost::shared_mutex>;

using unique_lock_type =
boost::unique_lock<boost::shared_mutex>;

struct state
{
File df;
Expand Down Expand Up @@ -429,7 +423,7 @@ class basic_store

bool
exists(detail::nhash_t h, void const* key,
shared_lock_type* lock, detail::bucket b, error_code& ec);
detail::shared_lock_type* lock, detail::bucket b, error_code& ec);

void
split(detail::bucket& b1, detail::bucket& b2,
Expand Down
134 changes: 112 additions & 22 deletions include/nudb/detail/arena.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
#ifndef NUDB_DETAIL_ARENA_HPP
#define NUDB_DETAIL_ARENA_HPP

#include <nudb/detail/mutex.hpp>
#include <boost/assert.hpp>
#include <algorithm>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <memory>

#include <beast/unit_test/dstream.hpp>

namespace nudb {
namespace detail {

Expand All @@ -29,11 +33,17 @@ namespace detail {
template<class = void>
class arena_t
{
using clock_type = std::chrono::steady_clock;
using time_point = typename clock_type::time_point;

//using clock_type =
class element;

std::size_t allocSize_;
std::size_t allocSize_ = 0;
std::size_t nalloc_ = 0;
element* used_ = nullptr;
element* free_ = nullptr;
time_point when_ = clock_type::now();

public:
arena_t(arena_t const&) = delete;
Expand All @@ -42,10 +52,9 @@ class arena_t

~arena_t();

arena_t(arena_t&& other);
arena_t() = default;

explicit
arena_t(std::size_t allocSize);
arena_t(arena_t&& other);

// Makes used blocks reusable
void
Expand All @@ -55,6 +64,11 @@ class arena_t
void
shrink_to_fit();

// Called every so often
void
periodic_activity(
detail::unique_lock_type& m);

std::uint8_t*
alloc(std::size_t n);

Expand All @@ -73,7 +87,6 @@ class arena_t
template<class _>
class arena_t<_>::element
{
private:
std::size_t const capacity_;
std::size_t used_ = 0;

Expand All @@ -82,7 +95,7 @@ class arena_t<_>::element

explicit
element(std::size_t allocSize)
: capacity_(allocSize - sizeof(*this))
: capacity_(allocSize)
{
}

Expand Down Expand Up @@ -136,20 +149,16 @@ template<class _>
arena_t<_>::
arena_t(arena_t&& other)
: allocSize_(other.allocSize_)
, nalloc_(other.nalloc_)
, used_(other.used_)
, free_(other.free_)
, when_(other.when_)
{
other.nalloc_ = 0;
other.used_ = nullptr;
other.free_ = nullptr;
}

template<class _>
arena_t<_>::
arena_t(std::size_t allocSize)
: allocSize_(allocSize)
{
if(allocSize <= sizeof(element))
throw std::domain_error("arena: bad alloc size");
other.when_ = clock_type::now();
other.allocSize_ = 0;
}

template<class _>
Expand All @@ -162,8 +171,16 @@ clear()
auto const e = used_;
used_ = used_->next;
e->clear();
e->next = free_;
free_ = e;
if(e->remain() == allocSize_)
{
e->next = free_;
free_ = e;
}
else
{
e->~element();
delete[] reinterpret_cast<std::uint8_t*>(e);
}
}
}

Expand All @@ -173,6 +190,73 @@ arena_t<_>::
shrink_to_fit()
{
dealloc(free_);
beast::unit_test::dstream dout{std::cout};
auto const size =
[](element* e)
{
std::size_t n = 0;
while(e)
{
++n;
e = e->next;
}
return n;
};
dout << "shrink_to_fit: alloc=" << allocSize_ <<
", nalloc=" << nalloc_ << ", used=" << size(used_) << "\n";
}

template<class _>
void
arena_t<_>::
periodic_activity(
detail::unique_lock_type& m)
{
using namespace std::chrono;
auto const now = clock_type::now();
auto const elapsed = now - when_;
if(elapsed < seconds{1})
return;
when_ = now;
if(! m.owns_lock())
m.lock();
auto const rate = static_cast<std::size_t>(std::ceil(
nalloc_ / duration_cast<duration<float>>(elapsed).count()));
beast::unit_test::dstream dout{std::cout};

auto const size =
[](element* e)
{
std::size_t n = 0;
while(e)
{
++n;
e = e->next;
}
return n;
};
if(rate >= allocSize_ * 2)
{
// adjust up
allocSize_ = std::max(rate, allocSize_ * 2);
dealloc(free_);
dout << " rate=" << rate << ", alloc=" << allocSize_ << " UP, "
"nalloc=" << nalloc_ << ", used=" << size(used_) << ", free=" << size(free_) << "\n";
}
else if(rate <= allocSize_ / 2)
{
// adjust down
allocSize_ /= 2;
dealloc(free_);
dout << " rate=" << rate << ", alloc=" << allocSize_ << " DOWN, "
"nalloc=" << nalloc_ << ", used=" << size(used_) << ", free=" << size(free_) << "\n";
}
else
{
dout << " rate=" << rate << ", alloc=" << allocSize_ << ", "
"nalloc=" << nalloc_ << ", used=" << size(used_) << ", free=" << size(free_) << "\n";
}
nalloc_ = 0;
}

template<class _>
Expand All @@ -184,22 +268,26 @@ alloc(std::size_t n)
BOOST_ASSERT(n != 0);
n = 8 *((n + 7) / 8);
if(used_ && used_->remain() >= n)
{
nalloc_ += n;
return used_->alloc(n);
}
if(free_ && free_->remain() >= n)
{
auto const e = free_;
free_ = free_->next;
e->next = used_;
used_ = e;
nalloc_ += n;
return used_->alloc(n);
}
auto const size = std::max(
allocSize_, sizeof(element) + n);
auto const size = std::max(allocSize_, n);
auto const e = reinterpret_cast<element*>(
new std::uint8_t[size]);
::new(e) element(size);
new std::uint8_t[sizeof(element) + size]);
::new(e) element{size};
e->next = used_;
used_ = e;
nalloc_ += n;
return used_->alloc(n);
}

Expand All @@ -208,9 +296,11 @@ void
swap(arena_t<_>& lhs, arena_t<_>& rhs)
{
using std::swap;
swap(lhs.allocSize_, rhs.allocSize_);
swap(lhs.nalloc_, rhs.nalloc_);
swap(lhs.used_, rhs.used_);
swap(lhs.free_, rhs.free_);
swap(lhs.when_, rhs.when_);
// allocSize_ not swapped, by design
}

template<class _>
Expand Down
9 changes: 0 additions & 9 deletions include/nudb/detail/cache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,6 @@ class cache_t
using value_type = std::pair<nbuck_t, bucket>;

private:
enum
{
// The arena's alloc size will be this
// multiple of the block size.
factor = 64
};

#if 0
using map_type = std::unordered_map<
nbuck_t, void*>;
Expand Down Expand Up @@ -142,7 +135,6 @@ cache_t<_>::
cache_t()
: key_size_(0)
, block_size_(0)
, arena_(32) // arbitrary small number
{
}

Expand All @@ -161,7 +153,6 @@ cache_t<_>::
cache_t(nsize_t key_size, nsize_t block_size)
: key_size_(key_size)
, block_size_(block_size)
, arena_(block_size * factor)
{
}

Expand Down
26 changes: 26 additions & 0 deletions include/nudb/detail/mutex.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef NUDB_DETAIL_MUTEX_HPP
#define NUDB_DETAIL_MUTEX_HPP

#include <boost/thread/lock_types.hpp>
#include <boost/thread/shared_mutex.hpp>

namespace nudb {
namespace detail {

using shared_lock_type =
boost::shared_lock<boost::shared_mutex>;

using unique_lock_type =
boost::unique_lock<boost::shared_mutex>;

} // detail
} // nudb

#endif
24 changes: 20 additions & 4 deletions include/nudb/detail/pool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
#include <nudb/detail/arena.hpp>
#include <nudb/detail/bucket.hpp>
#include <nudb/detail/format.hpp>
#include <nudb/detail/mutex.hpp>
#include <boost/assert.hpp>
#include <boost/thread/lock_types.hpp>
#include <cstdint>
#include <cstring>
#include <memory>
Expand Down Expand Up @@ -48,7 +50,8 @@ class pool_t

pool_t(pool_t&& other);

pool_t(nsize_t key_size, std::size_t alloc_size);
explicit
pool_t(nsize_t key_size);

iterator
begin()
Expand Down Expand Up @@ -88,6 +91,11 @@ class pool_t
void
shrink_to_fit();

// Called every so often
void
periodic_activity(
detail::unique_lock_type& m);

iterator
find(void const* key);

Expand Down Expand Up @@ -166,9 +174,8 @@ pool_t(pool_t&& other)

template<class _>
pool_t<_>::
pool_t(nsize_t key_size, std::size_t alloc_size)
: arena_(alloc_size)
, key_size_(key_size)
pool_t(nsize_t key_size)
: key_size_(key_size)
, map_(compare{key_size})
{
}
Expand All @@ -191,6 +198,15 @@ shrink_to_fit()
arena_.shrink_to_fit();
}

template<class _>
void
pool_t<_>::
periodic_activity(
detail::unique_lock_type& m)
{
arena_.periodic_activity(m);
}

template<class _>
auto
pool_t<_>::
Expand Down
Loading

0 comments on commit 517510a

Please sign in to comment.