mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-07 01:06:45 +08:00
optimize memory management and recycle strategy
This commit is contained in:
parent
a20a10c590
commit
8da0b32d0b
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <limits>
|
#include <limits> // std::numeric_limits
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <cstdlib>
|
|
||||||
#include <map>
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <limits> // std::numeric_limits
|
||||||
|
#include <cstdlib>
|
||||||
#include <cassert> // assert
|
#include <cassert> // assert
|
||||||
|
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
@ -41,7 +41,7 @@ public:
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept {
|
constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept {
|
||||||
return ((size - 1) & ~(alignment - 1)) + alignment;
|
return ( (size - 1) & ~(alignment - 1) ) + alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC_CONCEPT_(has_take, take(std::move(std::declval<Type>())));
|
IPC_CONCEPT_(has_take, take(std::move(std::declval<Type>())));
|
||||||
@ -49,8 +49,8 @@ IPC_CONCEPT_(has_take, take(std::move(std::declval<Type>())));
|
|||||||
class scope_alloc_base {
|
class scope_alloc_base {
|
||||||
protected:
|
protected:
|
||||||
struct block_t {
|
struct block_t {
|
||||||
block_t * next_;
|
|
||||||
std::size_t size_;
|
std::size_t size_;
|
||||||
|
block_t * next_;
|
||||||
} * head_ = nullptr, * tail_ = nullptr;
|
} * head_ = nullptr, * tail_ = nullptr;
|
||||||
|
|
||||||
enum : std::size_t {
|
enum : std::size_t {
|
||||||
@ -127,9 +127,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void* alloc(std::size_t size) {
|
void* alloc(std::size_t size) {
|
||||||
auto curr = static_cast<block_t*>(alloc_.alloc(size += aligned_block_size));
|
std::size_t real_size = aligned_block_size + size;
|
||||||
|
auto curr = static_cast<block_t*>(alloc_.alloc(real_size));
|
||||||
|
curr->size_ = real_size;
|
||||||
curr->next_ = head_;
|
curr->next_ = head_;
|
||||||
curr->size_ = size;
|
|
||||||
head_ = curr;
|
head_ = curr;
|
||||||
if (tail_ == nullptr) {
|
if (tail_ == nullptr) {
|
||||||
tail_ = curr;
|
tail_ = curr;
|
||||||
@ -272,11 +273,13 @@ public:
|
|||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template <std::size_t BaseSize = sizeof(void*) * 1024>
|
template <std::size_t BaseSize = sizeof(void*) * 1024,
|
||||||
|
std::size_t LimitSize = (std::numeric_limits<std::uint32_t>::max)()>
|
||||||
struct fixed_expand_policy {
|
struct fixed_expand_policy {
|
||||||
|
|
||||||
enum : std::size_t {
|
enum : std::size_t {
|
||||||
base_size = BaseSize
|
base_size = BaseSize,
|
||||||
|
limit_size = LimitSize
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr static std::size_t prev(std::size_t e) noexcept {
|
constexpr static std::size_t prev(std::size_t e) noexcept {
|
||||||
@ -289,7 +292,7 @@ struct fixed_expand_policy {
|
|||||||
|
|
||||||
static std::size_t next(std::size_t block_size, std::size_t & e) {
|
static std::size_t next(std::size_t block_size, std::size_t & e) {
|
||||||
auto n = ipc::detail::max<std::size_t>(block_size, base_size) * e;
|
auto n = ipc::detail::max<std::size_t>(block_size, base_size) * e;
|
||||||
e = next(e);
|
e = ipc::detail::min<std::size_t>(limit_size, next(e));
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -302,7 +305,7 @@ public:
|
|||||||
using base_t = detail::fixed_alloc<AllocP, ExpandP>;
|
using base_t = detail::fixed_alloc<AllocP, ExpandP>;
|
||||||
|
|
||||||
enum : std::size_t {
|
enum : std::size_t {
|
||||||
block_size = (ipc::detail::max)(BlockSize, sizeof(void*))
|
block_size = ipc::detail::max<std::size_t>(BlockSize, sizeof(void*))
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -334,35 +337,32 @@ namespace detail {
|
|||||||
|
|
||||||
class variable_alloc_base {
|
class variable_alloc_base {
|
||||||
protected:
|
protected:
|
||||||
struct head_t {
|
byte_t * head_ = nullptr, * tail_ = nullptr;
|
||||||
std::size_t free_;
|
|
||||||
} * head_ = nullptr;
|
|
||||||
|
|
||||||
enum : std::size_t {
|
|
||||||
aligned_head_size = aligned(sizeof(head_t), alignof(std::max_align_t))
|
|
||||||
};
|
|
||||||
|
|
||||||
static byte_t * buffer(head_t* p) {
|
|
||||||
return reinterpret_cast<byte_t*>(p) + aligned_head_size + p->free_;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool operator<(variable_alloc_base const & right) const {
|
void swap(variable_alloc_base & rhs) {
|
||||||
return remain() < right.remain();
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap(variable_alloc_base& rhs) {
|
|
||||||
std::swap(head_, rhs.head_);
|
std::swap(head_, rhs.head_);
|
||||||
|
std::swap(tail_, rhs.tail_);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t remain() const noexcept {
|
std::size_t remain() const noexcept {
|
||||||
return (head_ == nullptr) ? 0 : head_->free_;
|
return static_cast<std::size_t>(tail_ - head_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty() const noexcept {
|
bool empty() const noexcept {
|
||||||
return remain() == 0;
|
return remain() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void take(variable_alloc_base && rhs) {
|
||||||
|
if (remain() < rhs.remain()) {
|
||||||
|
// replace this by rhs
|
||||||
|
head_ = rhs.head_;
|
||||||
|
tail_ = rhs.tail_;
|
||||||
|
}
|
||||||
|
// discard rhs
|
||||||
|
rhs.head_ = rhs.tail_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void free(void* /*p*/) {}
|
void free(void* /*p*/) {}
|
||||||
void free(void* /*p*/, std::size_t) {}
|
void free(void* /*p*/, std::size_t) {}
|
||||||
};
|
};
|
||||||
@ -373,35 +373,15 @@ template <std::size_t ChunkSize = (sizeof(void*) * 1024), typename AllocP = scop
|
|||||||
class variable_alloc : public detail::variable_alloc_base {
|
class variable_alloc : public detail::variable_alloc_base {
|
||||||
public:
|
public:
|
||||||
using base_t = detail::variable_alloc_base;
|
using base_t = detail::variable_alloc_base;
|
||||||
using head_t = base_t::head_t;
|
|
||||||
using alloc_policy = AllocP;
|
using alloc_policy = AllocP;
|
||||||
|
|
||||||
|
enum : std::size_t {
|
||||||
|
aligned_chunk_size = detail::aligned(ChunkSize, alignof(std::max_align_t))
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename T>
|
|
||||||
struct fixed_alloc_t : public fixed_alloc<sizeof(T), alloc_policy> {};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using allocator = allocator_wrapper<T, fixed_alloc_t<T>>;
|
|
||||||
|
|
||||||
std::map<std::size_t, head_t*, std::greater<std::size_t>,
|
|
||||||
allocator<std::pair<const std::size_t, head_t*>>
|
|
||||||
> reserves_;
|
|
||||||
|
|
||||||
alloc_policy alloc_;
|
alloc_policy alloc_;
|
||||||
|
|
||||||
void take_base(variable_alloc && rhs) {
|
|
||||||
if (rhs.remain() > remain()) {
|
|
||||||
if (!empty()) {
|
|
||||||
reserves_.emplace(head_->free_, head_);
|
|
||||||
}
|
|
||||||
head_ = rhs.head_;
|
|
||||||
}
|
|
||||||
else if (!rhs.empty()) {
|
|
||||||
reserves_.emplace(rhs.head_->free_, rhs.head_);
|
|
||||||
}
|
|
||||||
rhs.head_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
variable_alloc() = default;
|
variable_alloc() = default;
|
||||||
|
|
||||||
@ -415,38 +395,28 @@ public:
|
|||||||
|
|
||||||
template <typename A = AllocP>
|
template <typename A = AllocP>
|
||||||
auto take(variable_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
|
auto take(variable_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
|
||||||
take_base(std::move(rhs));
|
base_t::take(std::move(rhs));
|
||||||
alloc_.take(std::move(rhs.alloc_));
|
alloc_.take(std::move(rhs.alloc_));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename A = AllocP>
|
|
||||||
auto take(variable_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> {
|
|
||||||
take_base(std::move(rhs));
|
|
||||||
}
|
|
||||||
|
|
||||||
void* alloc(std::size_t size) {
|
void* alloc(std::size_t size) {
|
||||||
if (size >= ChunkSize) {
|
/*
|
||||||
return alloc_.alloc(size);
|
* byte alignment is always alignof(std::max_align_t).
|
||||||
}
|
*/
|
||||||
|
size = detail::aligned(size, alignof(std::max_align_t));
|
||||||
|
void* ptr;
|
||||||
|
// size would never be 0 here
|
||||||
if (remain() < size) {
|
if (remain() < size) {
|
||||||
auto it = reserves_.begin();
|
std::size_t chunk_size = ipc::detail::max<std::size_t>(aligned_chunk_size, size);
|
||||||
if ((it == reserves_.end()) || (it->first < size)) {
|
ptr = alloc_.alloc(chunk_size);
|
||||||
head_ = static_cast<head_t*>(alloc_.alloc(ChunkSize + aligned_head_size));
|
tail_ = static_cast<byte_t*>(ptr) + chunk_size;
|
||||||
head_->free_ = ChunkSize - size;
|
head_ = tail_ - (chunk_size - size);
|
||||||
}
|
|
||||||
else {
|
|
||||||
auto temp = it->second;
|
|
||||||
temp->free_ -= size;
|
|
||||||
reserves_.erase(it);
|
|
||||||
if (remain() < temp->free_) {
|
|
||||||
head_ = temp;
|
|
||||||
}
|
|
||||||
else return base_t::buffer(temp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// size shouldn't be 0 here, otherwise behavior is undefined
|
else {
|
||||||
else head_->free_ -= size;
|
ptr = head_;
|
||||||
return base_t::buffer(head_);
|
head_ += size;
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -17,9 +17,12 @@
|
|||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace mem {
|
namespace mem {
|
||||||
|
|
||||||
using async_pool_alloc =
|
using async_pool_alloc = static_wrapper<variable_wrapper<async_wrapper<
|
||||||
static_wrapper<variable_wrapper<async_wrapper<detail::fixed_alloc<
|
detail::fixed_alloc<
|
||||||
static_alloc, fixed_expand_policy<> >>>>;
|
variable_alloc <sizeof(void*) * 1024 * 256>,
|
||||||
|
fixed_expand_policy<sizeof(void*) * 1024, sizeof(void*) * 1024 * 256>
|
||||||
|
>,
|
||||||
|
default_recycler >>>;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using allocator = allocator_wrapper<T, async_pool_alloc>;
|
using allocator = allocator_wrapper<T, async_pool_alloc>;
|
||||||
|
|||||||
@ -25,8 +25,17 @@ namespace mem {
|
|||||||
/// Thread-safe allocation wrapper
|
/// Thread-safe allocation wrapper
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
IPC_CONCEPT_(is_comparable, operator<(std::declval<Type>()));
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename AllocP, bool = detail::is_comparable<AllocP>::value>
|
||||||
|
class limited_recycler;
|
||||||
|
|
||||||
template <typename AllocP>
|
template <typename AllocP>
|
||||||
class limited_recycler {
|
class limited_recycler<AllocP, true> {
|
||||||
public:
|
public:
|
||||||
using alloc_policy = AllocP;
|
using alloc_policy = AllocP;
|
||||||
|
|
||||||
@ -35,10 +44,10 @@ protected:
|
|||||||
struct fixed_alloc_t : public fixed_alloc<sizeof(T)> {};
|
struct fixed_alloc_t : public fixed_alloc<sizeof(T)> {};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using allocator = allocator_wrapper<T, fixed_alloc_t<T>>;
|
using allocator = ipc::mem::allocator_wrapper<T, fixed_alloc_t<T>>;
|
||||||
|
|
||||||
std::set<alloc_policy, std::less<alloc_policy>,
|
std::multiset<alloc_policy, std::less<alloc_policy>,
|
||||||
allocator<alloc_policy>
|
allocator<alloc_policy>
|
||||||
> master_allocs_;
|
> master_allocs_;
|
||||||
|
|
||||||
ipc::spin_lock master_lock_;
|
ipc::spin_lock master_lock_;
|
||||||
@ -64,16 +73,13 @@ public:
|
|||||||
|
|
||||||
void collect(alloc_policy && alc) {
|
void collect(alloc_policy && alc) {
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
||||||
auto it = master_allocs_.find(alc);
|
if (master_allocs_.size() >= 32) {
|
||||||
if (it == master_allocs_.end()) {
|
take_first_do([](alloc_policy &) {}); // erase first
|
||||||
master_allocs_.emplace(std::move(alc));
|
|
||||||
}
|
}
|
||||||
else const_cast<alloc_policy&>(*it).swap(alc);
|
master_allocs_.emplace(std::move(alc));
|
||||||
// if (master_allocs_.size() <= 32) return;
|
|
||||||
// take_first_do([](alloc_policy &) {}); // erase first
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr static auto try_replenish(alloc_policy&, std::size_t) noexcept {}
|
constexpr auto try_replenish(alloc_policy&, std::size_t) noexcept {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename AllocP>
|
template <typename AllocP>
|
||||||
@ -125,10 +131,10 @@ class empty_recycler {
|
|||||||
public:
|
public:
|
||||||
using alloc_policy = AllocP;
|
using alloc_policy = AllocP;
|
||||||
|
|
||||||
constexpr static void swap(empty_recycler&) noexcept {}
|
constexpr void swap(empty_recycler&) noexcept {}
|
||||||
constexpr static void try_recover(alloc_policy&) noexcept {}
|
constexpr void try_recover(alloc_policy&) noexcept {}
|
||||||
constexpr static auto try_replenish(alloc_policy&, std::size_t) noexcept {}
|
constexpr auto try_replenish(alloc_policy&, std::size_t) noexcept {}
|
||||||
constexpr static void collect(alloc_policy&&) noexcept {}
|
constexpr void collect(alloc_policy&&) noexcept {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename AllocP,
|
template <typename AllocP,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user