From 0ef1d27521bf9577a76aa36dab00008b36c517be Mon Sep 17 00:00:00 2001 From: zhangyi Date: Tue, 2 Jul 2019 18:48:08 +0800 Subject: [PATCH] use chunk_variable_alloc as allocator-policy of static_async_fixed --- src/memory/alloc.h | 180 +++++++++++++++++++++--------------------- src/memory/resource.h | 40 +++++++++- src/memory/wrapper.h | 104 ++++++++++++++++++++---- 3 files changed, 216 insertions(+), 108 deletions(-) diff --git a/src/memory/alloc.h b/src/memory/alloc.h index d4f8fc6..63c20a5 100644 --- a/src/memory/alloc.h +++ b/src/memory/alloc.h @@ -14,8 +14,8 @@ namespace mem { class static_alloc { public: - static void clear() {} static void swap(static_alloc&) {} + static void clear() {} static void* alloc(std::size_t size) { return size ? std::malloc(size) : nullptr; @@ -36,12 +36,19 @@ public: namespace detail { +constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept { + return ((size - 1) & ~(alignment - 1)) + alignment; +} + class scope_alloc_base { protected: struct block_t { block_t* next_; + } * list_ = nullptr; + + enum : std::size_t { + aligned_block_size = aligned(sizeof(block_t), alignof(std::max_align_t)) }; - block_t* list_ = nullptr; public: void swap(scope_alloc_base & rhs) { @@ -63,33 +70,42 @@ public: private: alloc_policy alloc_; -public: - scope_alloc() = default; - - scope_alloc(scope_alloc&& rhs) { this->swap(rhs); } - scope_alloc& operator=(scope_alloc&& rhs) { this->swap(rhs); return (*this); } - - ~scope_alloc() { clear(); } - - void swap(scope_alloc& rhs) { - std::swap(this->alloc_, rhs.alloc_); - base_t::swap(rhs); - } - - void clear() { + void free_all() { while (list_ != nullptr) { auto curr = list_; list_ = list_->next_; alloc_.free(curr); } // now list_ is nullptr - alloc_.clear(); + } + +public: + scope_alloc() = default; + + scope_alloc(scope_alloc&& rhs) { this->swap(rhs); } + scope_alloc& operator=(scope_alloc&& rhs) { this->swap(rhs); return (*this); } + + ~scope_alloc() { free_all(); } + + template + void set_allocator(A && alc) { + alloc_ = std::forward(alc); + } + + void swap(scope_alloc& rhs) { + alloc_.swap(rhs.alloc_); + base_t::swap(rhs); + } + + void clear() { + free_all(); + alloc_.~alloc_policy(); } void* alloc(std::size_t size) { - auto curr = static_cast(alloc_.alloc(sizeof(block_t) + size)); + auto curr = static_cast(alloc_.alloc(aligned_block_size + size)); curr->next_ = list_; - return ((list_ = curr) + 1); + return (reinterpret_cast(list_ = curr) + aligned_block_size); } }; @@ -99,13 +115,6 @@ public: namespace detail { -template -struct fixed_expand_policy { - static std::size_t next(std::size_t & e) { - return (ipc::detail::max)(BlockSize, static_cast(2048)) * (e *= 2); - } -}; - class fixed_alloc_base { protected: std::size_t init_expand_; @@ -141,11 +150,18 @@ public: } }; +struct fixed_expand_policy { + template + IPC_CONSTEXPR_ static std::size_t next(std::size_t & e) { + return ipc::detail::max(BlockSize, (sizeof(void*) * 1024) / 2) * (e *= 2); + } +}; + } // namespace detail template class ExpandP = detail::fixed_expand_policy, - typename AllocP = scope_alloc<>> + typename AllocP = scope_alloc<>, + typename ExpandP = detail::fixed_expand_policy> class fixed_alloc : public detail::fixed_alloc_base { public: using base_t = detail::fixed_alloc_base; @@ -162,7 +178,7 @@ private: if (this->cursor_ != nullptr) { return this->cursor_; } - auto size = ExpandP::next(this->init_expand_); + auto size = ExpandP::template next(this->init_expand_); auto p = this->node_p(this->cursor_ = alloc_.alloc(size)); for (std::size_t i = 0; i < (size / block_size) - 1; ++i) p = this->node_p((*p) = reinterpret_cast(p) + block_size); @@ -175,16 +191,21 @@ public: this->init(init_expand); } - fixed_alloc(fixed_alloc&& rhs) { this->swap(rhs); } - fixed_alloc& operator=(fixed_alloc&& rhs) { this->swap(rhs); return (*this); } + fixed_alloc(fixed_alloc&& rhs) : fixed_alloc() { this->swap(rhs); } + fixed_alloc& operator=(fixed_alloc&& rhs) { this->swap(rhs); return (*this); } + + template + void set_allocator(A && alc) { + alloc_ = std::forward(alc); + } void swap(fixed_alloc& rhs) { - std::swap(this->alloc_, rhs.alloc_); + alloc_.swap(rhs.alloc_); base_t::swap(rhs); } void clear() { - alloc_.clear(); + alloc_.~alloc_policy(); this->init(this->init_expand_); } @@ -200,7 +221,7 @@ public: }; //////////////////////////////////////////////////////////////// -/// Variable-size blocks allocation +/// Variable-size blocks allocation (without alignment) //////////////////////////////////////////////////////////////// namespace detail { @@ -211,23 +232,19 @@ protected: head_t * next_; size_t size_; size_t free_; + } * head_ = nullptr; + + enum : std::size_t { + aligned_head_size = aligned(sizeof(head_t), alignof(std::max_align_t)) }; - char * head_, * tail_; - - void init() { - // makes chain to nullptr - head_ = tail_ = reinterpret_cast(sizeof(head_t)); - } - - head_t* chain() { - return reinterpret_cast(head_ - sizeof(head_t)); + static byte_t * buffer(head_t* p) { + return reinterpret_cast(p) + aligned_head_size; } public: void swap(variable_alloc_base& rhs) { std::swap(this->head_, rhs.head_); - std::swap(this->tail_, rhs.tail_); } void free(void* /*p*/) {} @@ -236,7 +253,7 @@ public: } // namespace detail -template > +template class variable_alloc : public detail::variable_alloc_base { public: using base_t = detail::variable_alloc_base; @@ -247,76 +264,61 @@ private: alloc_policy alloc_; head_t* alloc_head(std::size_t size) { - size = (ipc::detail::max)(ChunkSize, (ipc::detail::max)(size, sizeof(head_t))); + size = (ipc::detail::max)(ChunkSize, ipc::detail::max(size, aligned_head_size)); head_t* p = static_cast(alloc_.alloc(size)); - p->free_ = (p->size_ = size) - sizeof(head_t); + p->free_ = (p->size_ = size) - aligned_head_size; return p; } - void free_head(head_t* curr) { - alloc_.free(curr, curr->size_); - } - - std::size_t remain() const { - return (tail_ - head_); - } - void* alloc_new_chunk(std::size_t size) { - head_t* p = alloc_head(sizeof(head_t) + size); + head_t* p = alloc_head(aligned_head_size + size); if (p == nullptr) return nullptr; - head_t* list = chain(); - if (size > (ChunkSize - sizeof(head_t)) && list != nullptr) { - p->next_ = list->next_; - list->next_ = p; - char* head = reinterpret_cast(p + 1); - char* tail = head + p->free_ - size; - p->free_ = tail - head; - return tail; + if (size > (ChunkSize - aligned_head_size) && head_ != nullptr) { + p->next_ = head_->next_; + head_->next_ = p; + return base_t::buffer(p) + (p->free_ -= size); } - else { - p->next_ = list; - head_ = reinterpret_cast(p + 1); - tail_ = head_ + p->free_ - size; - p->free_ = remain(); - return tail_; + p->next_ = head_; + return base_t::buffer(head_ = p) + (p->free_ -= size); + } + + void free_all() { + while (head_ != nullptr) { + head_t* curr = head_; + head_ = head_->next_; + alloc_.free(curr, curr->size_); } + // now head_ is nullptr } public: - variable_alloc() { this->init(); } + variable_alloc() = default; variable_alloc(variable_alloc&& rhs) { this->swap(rhs); } variable_alloc& operator=(variable_alloc&& rhs) { this->swap(rhs); return (*this); } - ~variable_alloc() { clear(); } + ~variable_alloc() { free_all(); } + + template + void set_allocator(A && alc) { + alloc_ = std::forward(alc); + } void swap(variable_alloc& rhs) { - std::swap(this->alloc_, rhs.alloc_); + alloc_.swap(rhs.alloc_); base_t::swap(rhs); } void clear() { - head_t* list = chain(); - while (list != nullptr) { - head_t* curr = list; - list = list->next_; - free_head(curr); - } - alloc_.clear(); - this->init(); + free_all(); + alloc_.~alloc_policy(); } void* alloc(size_t size) { - if (remain() < size) { + if ((head_ == nullptr) || head_->free_ < size) { return alloc_new_chunk(size); } - char* buff = tail_ - size; - if (buff < head_) { - return alloc_new_chunk(size); - } - tail_ = buff; - chain()->free_ = remain(); - return tail_; + return base_t::buffer(head_) + (head_->free_ -= size); } }; diff --git a/src/memory/resource.h b/src/memory/resource.h index 97697a6..7002239 100644 --- a/src/memory/resource.h +++ b/src/memory/resource.h @@ -18,8 +18,44 @@ namespace ipc { namespace mem { template -using static_async_fixed = static_wrapper>>; -using async_pool_alloc = variable_wrapper; +using static_sync_fixed = static_wrapper>>; + +namespace detail { + +struct chunk_mapping_policy { + + enum : std::size_t { + base_size = sizeof(void*) * 1024 * 1024, /* 8MB(x64) */ + classes_size = 1 + }; + + constexpr static std::size_t classify(std::size_t size) { + return (size <= base_size) ? 0 : classes_size; + } +}; + +template +struct chunk_alloc_recoverer { +public: + using alloc_policy = AllocP; + + constexpr static void swap(chunk_alloc_recoverer &) {} + constexpr static void clear() {} + constexpr static void try_recover(alloc_policy &) {} + constexpr static void collect(alloc_policy &&) {} +}; + +} // namespace detail + +using static_chunk_alloc = variable_wrapper; +using chunk_variable_alloc = variable_alloc; + +template +using static_async_fixed = + static_wrapper, detail::chunk_alloc_recoverer>>; + +using async_pool_alloc = variable_wrapper; +//using async_pool_alloc = static_wrapper>; template using allocator = allocator_wrapper; diff --git a/src/memory/wrapper.h b/src/memory/wrapper.h index e4fbd7b..2b7856a 100644 --- a/src/memory/wrapper.h +++ b/src/memory/wrapper.h @@ -130,35 +130,65 @@ constexpr bool operator!=(const allocator_wrapper&, const allocator_w //////////////////////////////////////////////////////////////// template +class default_alloc_recoverer { +public: + using alloc_policy = AllocP; + +private: + ipc::spin_lock master_lock_; + std::vector master_allocs_; + +public: + void swap(default_alloc_recoverer& rhs) { + IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); + master_allocs_.swap(rhs.master_allocs_); + } + + void clear() { + IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); + master_allocs_.clear(); + } + + void try_recover(alloc_policy & alc) { + IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); + if (!master_allocs_.empty()) { + alc.swap(master_allocs_.back()); + master_allocs_.pop_back(); + } + } + + void collect(alloc_policy && alc) { + IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); + master_allocs_.emplace_back(std::move(alc)); + } +}; + +template class RecovererP = default_alloc_recoverer> class async_wrapper { public: using alloc_policy = AllocP; private: - spin_lock master_lock_; - std::vector master_allocs_; + RecovererP recoverer_; class alloc_proxy : public AllocP { async_wrapper * w_ = nullptr; public: - alloc_proxy(alloc_proxy&& rhs) + alloc_proxy(alloc_proxy && rhs) : AllocP(std::move(rhs)) {} - alloc_proxy(async_wrapper* w) : w_(w) { + alloc_proxy(async_wrapper* w) + : AllocP(), w_(w) { if (w_ == nullptr) return; - IPC_UNUSED_ auto guard = ipc::detail::unique_lock(w_->master_lock_); - if (!w_->master_allocs_.empty()) { - AllocP::swap(w_->master_allocs_.back()); - w_->master_allocs_.pop_back(); - } + w_->recoverer_.try_recover(*this); } ~alloc_proxy() { if (w_ == nullptr) return; - IPC_UNUSED_ auto guard = ipc::detail::unique_lock(w_->master_lock_); - w_->master_allocs_.emplace_back(std::move(*this)); + w_->recoverer_.collect(std::move(*this)); } }; @@ -170,13 +200,12 @@ private: } public: - ~async_wrapper() { - clear(); + void swap(async_wrapper& rhs) { + recoverer_.swap(rhs.recoverer_); } void clear() { - IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_); - master_allocs_.clear(); + recoverer_.clear(); } void* alloc(std::size_t size) { @@ -188,6 +217,42 @@ public: } }; +//////////////////////////////////////////////////////////////// +/// Thread-safe allocation wrapper (with spin_lock) +//////////////////////////////////////////////////////////////// + +template +class sync_wrapper { +public: + using alloc_policy = AllocP; + using mutex_type = MutexT; + +private: + mutex_type lock_; + alloc_policy alloc_; + +public: + void swap(sync_wrapper& rhs) { + IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); + alloc_.swap(rhs.alloc_); + } + + void clear() { + IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); + alloc_.~alloc_policy(); + } + + void* alloc(std::size_t size) { + IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); + return alloc_.alloc(size); + } + + void free(void* p, std::size_t size) { + IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_); + alloc_.free(p, size); + } +}; + //////////////////////////////////////////////////////////////// /// Static allocation wrapper //////////////////////////////////////////////////////////////// @@ -202,6 +267,8 @@ public: return alloc; } + static void swap(static_wrapper&) {} + static void clear() { instance().clear(); } @@ -246,8 +313,8 @@ const std::size_t default_mapping_policy::table[default_mapping_policy::cl }; template