try pool_alloc (TBD)

This commit is contained in:
mutouyun 2018-12-29 00:51:31 +08:00
parent 47236c9f24
commit 02a5920697
9 changed files with 383 additions and 16 deletions

View File

@ -26,7 +26,10 @@ HEADERS += \
../include/tls_pointer.h \ ../include/tls_pointer.h \
../src/channel.inc \ ../src/channel.inc \
../src/route.inc \ ../src/route.inc \
../src/id_pool.inc ../src/id_pool.inc \
../src/memory/alloc.hpp \
../src/memory/wrapper.hpp \
../src/memory/resource.hpp
SOURCES += \ SOURCES += \
../src/shm.cpp \ ../src/shm.cpp \

View File

@ -7,7 +7,6 @@
#include <atomic> #include <atomic>
#include <tuple> #include <tuple>
#include <thread> #include <thread>
#include <chrono>
#include "def.h" #include "def.h"
#include "circ_elem_array.h" #include "circ_elem_array.h"
@ -98,7 +97,7 @@ public:
template <typename F> template <typename F>
static queue* multi_wait_for(F&& upd) noexcept { static queue* multi_wait_for(F&& upd) noexcept {
for (unsigned k = 0;; ++k) { while (1) {
auto [ques, size] = upd(); auto [ques, size] = upd();
for (std::size_t i = 0; i < static_cast<std::size_t>(size); ++i) { for (std::size_t i = 0; i < static_cast<std::size_t>(size); ++i) {
queue* que = ques[i]; queue* que = ques[i];
@ -108,9 +107,7 @@ public:
return que; return que;
} }
} }
if (k < 1024) std::this_thread::yield(); std::this_thread::yield();
// yielding => sleeping
else std::this_thread::sleep_for(std::chrono::milliseconds(1));
} }
} }

View File

@ -1,6 +1,5 @@
#include "ipc.h" #include "ipc.h"
#include <unordered_map>
#include <type_traits> #include <type_traits>
#include <cstring> #include <cstring>
#include <algorithm> #include <algorithm>
@ -12,6 +11,8 @@
#include "shm.h" #include "shm.h"
#include "tls_pointer.h" #include "tls_pointer.h"
#include "memory/resource.hpp"
namespace { namespace {
using namespace ipc; using namespace ipc;
@ -55,7 +56,7 @@ inline auto& recv_cache() {
https://developercommunity.visualstudio.com/content/problem/124121/thread-local-variables-fail-to-be-initialized-when.html 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 https://software.intel.com/en-us/forums/intel-c-compiler/topic/684827
*/ */
static tls::pointer<std::unordered_map<msg_id_t, buff_t>> rc; static tls::pointer<memory::unordered_map<msg_id_t, buff_t>> rc;
return *rc.create(); return *rc.create();
} }

171
src/memory/alloc.hpp Normal file
View File

@ -0,0 +1,171 @@
#pragma once
#include <limits>
#include <algorithm>
#include <cstdlib>
#include "def.h"
namespace ipc {
namespace memory {
struct static_alloc {
static constexpr std::size_t remain() {
return (std::numeric_limits<std::size_t>::max)();
}
static constexpr void clear() {}
static void* alloc(std::size_t size) {
return size ? std::malloc(size) : nullptr;
}
static void free(void* p) {
std::free(p);
}
static void free(void* p, std::size_t /*size*/) {
free(p);
}
};
////////////////////////////////////////////////////////////////
/// Scope allocation -- The destructor will release all allocated blocks.
////////////////////////////////////////////////////////////////
template <typename AllocP = static_alloc>
class scope_alloc {
public:
using alloc_policy = AllocP;
private:
alloc_policy alloc_;
struct block_t {
block_t* next_;
};
block_t* list_ = nullptr;
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(); }
public:
void swap(scope_alloc& rhs) {
std::swap(this->alloc_, rhs.alloc_);
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_;
list_ = list_->next_;
alloc_.free(curr);
}
// now list_ is nullptr
alloc_.clear();
}
void* alloc(std::size_t size) {
auto curr = static_cast<block_t*>(alloc_.alloc(sizeof(block_t) + size));
curr->next_ = list_;
return ((list_ = curr) + 1);
}
void free(void* /*p*/) {}
};
////////////////////////////////////////////////////////////////
/// Fixed-size blocks allocation
////////////////////////////////////////////////////////////////
template <std::size_t BlockSize, typename AllocP = scope_alloc<>>
class fixed_pool {
public:
using alloc_policy = AllocP;
enum : std::size_t {
block_size = (std::max)(BlockSize, sizeof(void*))
};
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_));
for (std::size_t i = 0; i < iter_ - 1; ++i)
p = reinterpret_cast<void**>((*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);
}
fixed_pool(fixed_pool&& rhs) { this->swap(rhs); }
fixed_pool& operator=(fixed_pool&& rhs) { this->swap(rhs); return (*this); }
~fixed_pool() { clear(); }
public:
void swap(fixed_pool& rhs) {
std::swap(this->alloc_ , rhs.alloc_);
std::swap(this->init_expand_, rhs.init_expand_);
std::swap(this->iter_ , rhs.iter_);
std::swap(this->cursor_ , rhs.cursor_);
}
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;
}
void clear() {
alloc_.clear();
init(init_expand_);
}
void* alloc() {
if (cursor_ == nullptr) expand();
void* p = cursor_;
cursor_ = *reinterpret_cast<void**>(p);
return p;
}
void free(void* p) {
if (p == nullptr) return;
*reinterpret_cast<void**>(p) = cursor_;
cursor_ = p;
}
};
} // namespace memory
} // namespace ipc

85
src/memory/resource.hpp Normal file
View File

@ -0,0 +1,85 @@
#pragma once
#include <type_traits>
#include <initializer_list>
#include <limits>
#include <utility>
#include <functional>
#include <unordered_map>
#include "tls_pointer.h"
#include "memory/alloc.hpp"
#include "memory/wrapper.hpp"
namespace ipc {
namespace memory {
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))...
};
}
} // namespace detail
class pool_alloc {
private:
template <std::size_t Size>
static auto& fixed() {
static tls::pointer<fixed_pool<Size>> fp;
return *fp.create();
}
template <typename F>
static void 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<
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>()); });
}
public:
pool_alloc() = default;
pool_alloc(const pool_alloc&) = default;
pool_alloc& operator=(const pool_alloc&) = default;
pool_alloc(pool_alloc&&) = default;
pool_alloc& operator=(pool_alloc&&) = default;
static constexpr std::size_t remain() {
return (std::numeric_limits<std::size_t>::max)();
}
static constexpr void clear() {}
static void* alloc(std::size_t size) {
void* p;
choose(size, [&p](auto& fp) { p = fp.alloc(); });
return p;
}
static void free(void* p, std::size_t size) {
choose(size, [p](auto& fp) { fp.free(p); });
}
};
template <typename T>
using allocator = allocator_wrapper<T, pool_alloc>;
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
} // namespace ipc

110
src/memory/wrapper.hpp Normal file
View File

@ -0,0 +1,110 @@
#pragma once
#include <utility>
#include <limits>
#include <new>
namespace ipc {
namespace memory {
////////////////////////////////////////////////////////////////
/// The allocator wrapper class
////////////////////////////////////////////////////////////////
template <typename T, typename AllocP>
class allocator_wrapper {
template <typename U, typename AllocU>
friend class allocator_wrapper;
public:
// type definitions
typedef T value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef std::size_t size_type;
typedef AllocP alloc_policy;
private:
alloc_policy alloc_;
public:
allocator_wrapper(void) noexcept = default;
allocator_wrapper(const allocator_wrapper<T, AllocP>& rhs) noexcept
: alloc_(rhs.alloc_)
{}
template <typename U>
allocator_wrapper(const allocator_wrapper<U, AllocP>& rhs) noexcept
: alloc_(rhs.alloc_)
{}
allocator_wrapper(allocator_wrapper<T, AllocP>&& rhs) noexcept
: alloc_(std::move(rhs.alloc_))
{}
template <typename U>
allocator_wrapper(allocator_wrapper<U, AllocP>&& rhs) noexcept
: alloc_(std::move(rhs.alloc_))
{}
allocator_wrapper(const AllocP& rhs) noexcept
: alloc_(rhs)
{}
allocator_wrapper(AllocP&& rhs) noexcept
: alloc_(std::move(rhs))
{}
public:
// the other type of std_allocator
template <typename U>
struct rebind { typedef allocator_wrapper<U, AllocP> other; };
constexpr size_type max_size(void) const noexcept {
return (std::numeric_limits<size_type>::max)() / sizeof(T);
}
public:
pointer allocate(size_type count) noexcept {
if (count == 0) return nullptr;
if (count > this->max_size()) return nullptr;
return static_cast<pointer>(alloc_.alloc(count * sizeof(T)));
}
void deallocate(pointer p, size_type count) {
alloc_.free(p, count * sizeof(T));
}
template <typename... P>
static void construct(pointer p, P&&... params) {
::new (static_cast<void*>(p)) T(std::forward<P>(params)...);
}
static void destroy(pointer p) {
p->~T();
}
};
template <class AllocP>
class allocator_wrapper<void, AllocP> {
public:
typedef void value_type;
typedef AllocP alloc_policy;
};
template <typename T, typename U, class AllocP>
constexpr bool operator==(const allocator_wrapper<T, AllocP>&, const allocator_wrapper<U, AllocP>&) noexcept {
return true;
}
template <typename T, typename U, class AllocP>
constexpr bool operator!=(const allocator_wrapper<T, AllocP>&, const allocator_wrapper<U, AllocP>&) noexcept {
return false;
}
} // namespace memory
} // namespace ipc

View File

@ -6,11 +6,11 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <unordered_map>
#include <atomic> #include <atomic>
#include <string> #include <string>
#include "tls_pointer.h" #include "tls_pointer.h"
#include "memory/resource.hpp"
namespace { namespace {
@ -25,7 +25,7 @@ constexpr void* mem_of(void* mem) {
} }
inline auto& m2h() { inline auto& m2h() {
static ipc::tls::pointer<std::unordered_map<void*, std::string>> cache; static ipc::tls::pointer<ipc::memory::unordered_map<void*, std::string>> cache;
return *cache.create(); return *cache.create();
} }

View File

@ -7,10 +7,10 @@
#include <locale> #include <locale>
#include <codecvt> #include <codecvt>
#include <utility> #include <utility>
#include <unordered_map>
#include "def.h" #include "def.h"
#include "tls_pointer.h" #include "tls_pointer.h"
#include "memory/resource.hpp"
namespace { namespace {
@ -28,7 +28,7 @@ constexpr auto to_tchar(std::string && str) -> IsSame<T, std::wstring> {
} }
inline auto& m2h() { inline auto& m2h() {
static ipc::tls::pointer<std::unordered_map<void*, HANDLE>> cache; static ipc::tls::pointer<ipc::memory::unordered_map<void*, HANDLE>> cache;
return *cache.create(); return *cache.create();
} }

View File

@ -304,10 +304,10 @@ void test_lock_performance() {
} }
void Unit::test_rw_lock() { void Unit::test_rw_lock() {
test_lock_performance<1, 1>(); // test_lock_performance<1, 1>();
test_lock_performance<4, 4>(); // test_lock_performance<4, 4>();
test_lock_performance<1, 8>(); // test_lock_performance<1, 8>();
test_lock_performance<8, 1>(); // test_lock_performance<8, 1>();
} }
void Unit::test_send_recv() { void Unit::test_send_recv() {