pool_alloc (TBD)

This commit is contained in:
mutouyun 2018-12-29 18:31:01 +08:00
parent 812d4b3be5
commit 8c75f8ad84
8 changed files with 201 additions and 81 deletions

View File

@ -13,7 +13,7 @@ A high-performance inter-process communication using shared memory on Linux/Wind
## Performance
| | Environment |
| Environment | Value |
| ------ | ------ |
| Device | Lenovo ThinkPad T450 |
| CPU | Intel(R) Core(TM) i5-4300U @ 2.5 GHz |

View File

@ -4,8 +4,8 @@
#include <string>
#include <array>
#include <limits>
#include <shared_mutex>
#include <mutex>
#include <shared_mutex>
#include <thread>
#include <memory>
#include <utility>

View File

@ -46,6 +46,29 @@ constexpr queue_t* queue_of(handle_t h) {
return static_cast<queue_t*>(h);
}
using cache_t = mem::vector<byte_t>;
template <std::size_t N>
cache_t make_cache(byte_t const (& data)[N]) {
return {
static_cast<buff_t::value_type const *>(data),
static_cast<buff_t::value_type const *>(data) + N
};
}
template <typename T>
using remove_cv_ref_t = std::remove_cv_t<std::remove_reference_t<T>>;
template <typename Cache>
constexpr auto to_buff(Cache&& cac) -> Requires<std::is_same<remove_cv_ref_t<Cache>, buff_t>::value, buff_t> {
return cac;
}
template <typename Cache>
auto to_buff(Cache&& cac) -> Requires<!std::is_same<remove_cv_ref_t<Cache>, buff_t>::value, buff_t> {
return make_buff(cac.data(), cac.size());
}
inline auto& recv_cache() {
/*
<Remarks> thread_local may have some bugs.
@ -56,12 +79,12 @@ inline auto& recv_cache() {
https://developercommunity.visualstudio.com/content/problem/124121/thread-local-variables-fail-to-be-initialized-when.html
https://software.intel.com/en-us/forums/intel-c-compiler/topic/684827
*/
static tls::pointer<memory::unordered_map<msg_id_t, buff_t>> rc;
static tls::pointer<mem::unordered_map<msg_id_t, cache_t>> rc;
return *rc.create();
}
inline auto& queues_cache() {
static tls::pointer<std::vector<queue_t*>> qc;
static tls::pointer<mem::vector<queue_t*>> qc;
return *qc.create();
}
@ -168,7 +191,7 @@ buff_t multi_recv(F&& upd) {
return make_buff(msg.data_, remain);
}
// cache the first message fragment
else rc.emplace(msg.id_, make_buff(msg.data_));
else rc.emplace(msg.id_, make_cache(msg.data_));
}
// has cached before this message
else {
@ -177,9 +200,9 @@ buff_t multi_recv(F&& upd) {
if (msg.remain_ <= 0) {
cache.insert(cache.end(), msg.data_, msg.data_ + remain);
// finish this message, erase it from cache
auto buf = std::move(cache);
auto cac = std::move(cache);
rc.erase(cache_it);
return buf;
return to_buff(std::move(cac));
}
// there are remain datas after this message
cache.insert(cache.end(), msg.data_, msg.data_ + data_length);

View File

@ -2,19 +2,22 @@
#include <limits>
#include <algorithm>
#include <utility>
#include <cstdlib>
#include "def.h"
namespace ipc {
namespace memory {
namespace mem {
struct static_alloc {
class static_alloc {
public:
static constexpr std::size_t remain() {
return (std::numeric_limits<std::size_t>::max)();
}
static constexpr void clear() {}
static constexpr void swap(static_alloc&) {}
static void* alloc(std::size_t size) {
return size ? std::malloc(size) : nullptr;
@ -33,19 +36,40 @@ struct static_alloc {
/// Scope allocation -- The destructor will release all allocated blocks.
////////////////////////////////////////////////////////////////
namespace detail {
class scope_alloc_base {
protected:
struct block_t {
block_t* next_;
};
block_t* list_ = nullptr;
public:
std::size_t remain() const {
std::size_t c = 0;
auto curr = list_;
while (curr != nullptr) {
++c;
curr = curr->next_;
}
return c;
}
void free(void* /*p*/) {}
void free(void* /*p*/, std::size_t) {}
};
} // namespace detail
template <typename AllocP = static_alloc>
class scope_alloc {
class scope_alloc : public detail::scope_alloc_base {
public:
using alloc_policy = AllocP;
private:
alloc_policy alloc_;
struct block_t {
block_t* next_;
};
block_t* list_ = nullptr;
public:
scope_alloc() = default;
@ -60,16 +84,6 @@ public:
std::swap(this->list_ , rhs.list_);
}
std::size_t remain() const {
std::size_t c = 0;
auto curr = list_;
while (curr != nullptr) {
++c;
curr = curr->next_;
}
return c;
}
void clear() {
while (list_ != nullptr) {
auto curr = list_;
@ -85,16 +99,59 @@ public:
curr->next_ = list_;
return ((list_ = curr) + 1);
}
void free(void* /*p*/) {}
};
////////////////////////////////////////////////////////////////
/// Fixed-size blocks allocation
////////////////////////////////////////////////////////////////
namespace detail {
class fixed_pool_base {
protected:
std::size_t init_expand_;
std::size_t iter_;
void* cursor_;
void init(std::size_t init_expand) {
iter_ = init_expand_ = init_expand;
cursor_ = nullptr;
}
static void** node_p(void* node) {
return reinterpret_cast<void**>(node);
}
static auto& next(void* node) {
return *node_p(node);
}
public:
std::size_t remain() const {
std::size_t c = 0;
void* curr = cursor_;
while (curr != nullptr) {
++c;
curr = next(curr);
}
return c;
}
void free(void* p) {
if (p == nullptr) return;
next(p) = cursor_;
cursor_ = p;
}
void free(void* p, std::size_t) {
free(p);
}
};
} // namespace detail
template <std::size_t BlockSize, typename AllocP = scope_alloc<>>
class fixed_pool {
class fixed_pool : public detail::fixed_pool_base {
public:
using alloc_policy = AllocP;
@ -104,22 +161,15 @@ public:
private:
alloc_policy alloc_;
std::size_t init_expand_, iter_;
void* cursor_;
void expand() {
void** p = reinterpret_cast<void**>(cursor_ = alloc_.alloc(block_size * iter_));
auto p = node_p(cursor_ = alloc_.alloc(block_size * iter_));
for (std::size_t i = 0; i < iter_ - 1; ++i)
p = reinterpret_cast<void**>((*p) = reinterpret_cast<byte_t*>(p) + block_size);
p = node_p((*p) = reinterpret_cast<byte_t*>(p) + block_size);
(*p) = nullptr;
iter_ *= 2;
}
void init(std::size_t init_expand) {
iter_ = init_expand_ = init_expand;
cursor_ = nullptr;
}
public:
explicit fixed_pool(std::size_t init_expand = 1) {
init(init_expand);
@ -139,13 +189,7 @@ public:
}
std::size_t remain() const {
std::size_t c = 0;
void* curr = cursor_;
while (curr != nullptr) {
++c;
curr = *reinterpret_cast<void**>(curr); // curr = next
}
return c * block_size;
return detail::fixed_pool_base::remain() * block_size;
}
void clear() {
@ -156,16 +200,14 @@ public:
void* alloc() {
if (cursor_ == nullptr) expand();
void* p = cursor_;
cursor_ = *reinterpret_cast<void**>(p);
cursor_ = next(p);
return p;
}
void free(void* p) {
if (p == nullptr) return;
*reinterpret_cast<void**>(p) = cursor_;
cursor_ = p;
void* alloc(std::size_t) {
return alloc();
}
};
} // namespace memory
} // namespace mem
} // namespace ipc

View File

@ -1,27 +1,31 @@
#pragma once
#include <type_traits>
#include <initializer_list>
#include <limits>
#include <utility>
#include <functional>
#include <unordered_map>
#include "tls_pointer.h"
#include <vector>
#include "memory/alloc.hpp"
#include "memory/wrapper.hpp"
#include "tls_pointer.h"
namespace ipc {
namespace memory {
namespace mem {
namespace detail {
template <typename Comp, typename F, std::size_t...I>
void switch_constexpr(std::size_t i, std::index_sequence<I...>, F&& f) {
[[maybe_unused]] std::initializer_list<int> expand {
(Comp{}(i, I) && (f(std::integral_constant<size_t, I>{}), 0))...
};
template <typename F, typename D>
constexpr decltype(auto) switch_constexpr(std::size_t /*i*/, std::index_sequence<>, F&& /*f*/, D&& def) {
return def();
}
template <typename F, typename D, std::size_t N, std::size_t...I>
constexpr decltype(auto) switch_constexpr(std::size_t i, std::index_sequence<N, I...>, F&& f, D&& def) {
return (i == N) ? f(std::integral_constant<size_t, N>{}) :
switch_constexpr(i, std::index_sequence<I...>{}, f, def);
}
} // namespace detail
@ -35,18 +39,21 @@ private:
}
template <typename F>
static void choose(std::size_t size, F&& f) {
static decltype(auto) choose(std::size_t size, F&& f) {
enum : std::size_t { base_size = sizeof(void*) };
detail::switch_constexpr<std::less_equal<std::size_t>>(size, std::index_sequence<
size = ((size - 1) & (~(base_size - 1))) + base_size;
return detail::switch_constexpr(size, std::index_sequence<
base_size , base_size * 2 ,
base_size * 4 , base_size * 6 ,
base_size * 8 , base_size * 12,
base_size * 16, base_size * 20,
base_size * 24, base_size * 28,
base_size * 32, base_size * 40,
base_size * 48, base_size * 56,
base_size * 64
>{}, [&f](auto index) { f(fixed<decltype(index)::value>()); });
base_size * 3 , base_size * 4 ,
base_size * 5 , base_size * 6 ,
base_size * 7 , base_size * 8 ,
base_size * 9 , base_size * 10,
base_size * 11, base_size * 12,
base_size * 13, base_size * 14,
base_size * 15, base_size * 16
>{}, [&f](auto index) {
return f(fixed<decltype(index)::value>());
}, [&f] { return f(static_alloc{}); });
}
public:
@ -63,13 +70,11 @@ public:
static constexpr void clear() {}
static void* alloc(std::size_t size) {
void* p;
choose(size, [&p](auto& fp) { p = fp.alloc(); });
return p;
return choose(size, [size](auto&& fp) { return fp.alloc(size); });
}
static void free(void* p, std::size_t size) {
choose(size, [p](auto& fp) { fp.free(p); });
choose(size, [p](auto&& fp) { fp.free(p); });
}
};
@ -81,5 +86,8 @@ using unordered_map = std::unordered_map<
Key, T//, std::hash<Key>, std::equal_to<Key>, allocator<std::pair<const Key, T>>
>;
} // namespace memory
template <typename T>
using vector = std::vector<T/*, allocator<T>*/>;
} // namespace mem
} // namespace ipc

View File

@ -1,14 +1,61 @@
#pragma once
#include <utility>
#include <limits>
#include <new>
#include <mutex>
#include <shared_mutex>
#include <vector>
#include <utility>
#include "rw_lock.h"
#include "tls_pointer.h"
namespace ipc {
namespace memory {
namespace mem {
////////////////////////////////////////////////////////////////
/// The allocator wrapper class
/// Thread-safe allocation wrapper
////////////////////////////////////////////////////////////////
template <typename AllocP>
class synchronized_pool {
public:
using alloc_policy = AllocP;
private:
rw_lock lc_;
std::vector<alloc_policy*> allocs_;
struct alloc_t {
synchronized_pool* t_;
alloc_policy* alc_;
alloc_t(synchronized_pool* t)
: t_ { t }
{}
~alloc_t() {
}
};
public:
static constexpr std::size_t remain() {
return (std::numeric_limits<std::size_t>::max)();
}
static void* alloc() {
static tls::pointer<alloc_policy> alc;
return nullptr;
}
static void* alloc(std::size_t) {
return alloc();
}
};
////////////////////////////////////////////////////////////////
/// The allocator wrapper class for STL
////////////////////////////////////////////////////////////////
template <typename T, typename AllocP>
@ -106,5 +153,5 @@ constexpr bool operator!=(const allocator_wrapper<T, AllocP>&, const allocator_w
return false;
}
} // namespace memory
} // namespace mem
} // namespace ipc

View File

@ -25,7 +25,7 @@ constexpr void* mem_of(void* mem) {
}
inline auto& m2h() {
static ipc::tls::pointer<ipc::memory::unordered_map<void*, std::string>> cache;
static ipc::tls::pointer<ipc::mem::unordered_map<void*, std::string>> cache;
return *cache.create();
}

View File

@ -28,7 +28,7 @@ constexpr auto to_tchar(std::string && str) -> IsSame<T, std::wstring> {
}
inline auto& m2h() {
static ipc::tls::pointer<ipc::memory::unordered_map<void*, HANDLE>> cache;
static ipc::tls::pointer<ipc::mem::unordered_map<void*, HANDLE>> cache;
return *cache.create();
}