mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-06 16:56:45 +08:00
pool_alloc (TBD)
This commit is contained in:
parent
812d4b3be5
commit
8c75f8ad84
@ -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 |
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <shared_mutex>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
33
src/ipc.cpp
33
src/ipc.cpp
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,23 +70,24 @@ 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); });
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using allocator = allocator_wrapper<T, pool_alloc>;
|
||||
|
||||
template<typename Key, typename T>
|
||||
template <typename Key, typename T>
|
||||
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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user