modify test cases of mem;

sync_pool_alloc => async_pool_alloc, synchronized => async_wrapper; statical => static_wrapper;
optimize ipc::mem::allocator;
use std::hardware_destructive_interference_size for cache_line_size (TBD);
simplified codes.
This commit is contained in:
mutouyun 2019-06-30 12:02:12 +08:00
parent 0beb0b4a13
commit 36d85e4b41
12 changed files with 91 additions and 120 deletions

Binary file not shown.

View File

@ -12,10 +12,6 @@
namespace ipc {
namespace circ {
enum {
cache_line_size = 64
};
using u1_t = ipc::uint_t<8>;
using u2_t = ipc::uint_t<32>;

0
src/ipc.cpp Executable file → Normal file
View File

View File

@ -2,9 +2,6 @@
#include <algorithm>
#include <utility>
#include <atomic>
#include <thread>
#include <mutex>
#include <cstdlib>
#include "def.h"
@ -31,10 +28,6 @@ public:
static void free(void* p, std::size_t /*size*/) {
free(p);
}
static std::size_t size_of(void* /*p*/) {
return 0;
}
};
////////////////////////////////////////////////////////////////
@ -54,8 +47,8 @@ public:
void free(void* /*p*/) {}
void free(void* /*p*/, std::size_t) {}
constexpr std::size_t size_of(void* /*p*/) const {
return 0;
void swap(scope_alloc_base & rhs) {
std::swap(this->list_, rhs.list_);
}
};
@ -64,6 +57,7 @@ public:
template <typename AllocP = static_alloc>
class scope_alloc : public detail::scope_alloc_base {
public:
using base_t = detail::scope_alloc_base;
using alloc_policy = AllocP;
private:
@ -80,7 +74,7 @@ public:
public:
void swap(scope_alloc& rhs) {
std::swap(this->alloc_, rhs.alloc_);
std::swap(this->list_ , rhs.list_);
base_t::swap(rhs);
}
void clear() {
@ -106,6 +100,14 @@ public:
namespace detail {
template <std::size_t BlockSize>
struct fixed_expand_policy {
static std::size_t next(std::size_t & e) {
static constexpr std::size_t basic_size = (ipc::detail::max)(BlockSize, static_cast<std::size_t>(2048));
return basic_size * (e *= 2);
}
};
class fixed_alloc_base {
protected:
std::size_t init_expand_;
@ -113,7 +115,7 @@ protected:
void init(std::size_t init_expand) {
init_expand_ = init_expand;
cursor_ = nullptr;
cursor_ = nullptr;
}
static void** node_p(void* node) {
@ -127,7 +129,7 @@ protected:
public:
void swap(fixed_alloc_base& rhs) {
std::swap(this->init_expand_, rhs.init_expand_);
std::swap(this->cursor_ , rhs.cursor_ );
std::swap(this->cursor_ , rhs.cursor_);
}
void free(void* p) {
@ -143,7 +145,7 @@ public:
} // namespace detail
template <std::size_t BlockSize, typename AllocP = scope_alloc<>>
template <std::size_t BlockSize, template <std::size_t> class ExpandP = detail::fixed_expand_policy, typename AllocP = scope_alloc<>>
class fixed_alloc : public detail::fixed_alloc_base {
public:
using base_t = detail::fixed_alloc_base;
@ -160,9 +162,9 @@ private:
if (this->cursor_ != nullptr) {
return this->cursor_;
}
auto p = this->node_p(this->cursor_ = alloc_.alloc(block_size));
auto size = alloc_.size_of(p);
if (size > 0) for (std::size_t i = 0; i < (size / block_size) - 1; ++i)
auto size = ExpandP<block_size>::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<byte_t*>(p) + block_size);
(*p) = nullptr;
return this->cursor_;
@ -187,10 +189,6 @@ public:
this->init(this->init_expand_);
}
constexpr std::size_t size_of(void* /*p*/) const {
return block_size;
}
void* alloc() {
void* p = try_expand();
this->cursor_ = this->next(p);
@ -202,14 +200,5 @@ public:
}
};
////////////////////////////////////////////////////////////////
/// page memory allocation
////////////////////////////////////////////////////////////////
using page_alloc = fixed_alloc<4096>;
template <std::size_t BlockSize>
using page_fixed_alloc = fixed_alloc<BlockSize, page_alloc>;
} // namespace mem
} // namespace ipc

View File

@ -13,14 +13,6 @@ enum : std::size_t {
base_size = sizeof(void*)
};
template <std::size_t Size, template <std::size_t> class FixedAlloc>
struct fixed {
static auto& pool() {
static FixedAlloc<Size> pool;
return pool;
}
};
#if __cplusplus >= 201703L
constexpr std::size_t classify(std::size_t size) {
constexpr std::size_t mapping[] = {
@ -51,18 +43,18 @@ inline std::size_t classify(std::size_t size) {
template <template <std::size_t> class Fixed, typename F>
decltype(auto) choose(std::size_t size, F&& f) {
return ipc::detail::static_switch<32>(classify(size), [&f](auto index) {
return f(Fixed<(decltype(index)::value + 1) * base_size>::pool());
return f(Fixed<(decltype(index)::value + 1) * base_size>::instance());
}, [&f] {
return f(static_alloc{});
});
}
template <template <std::size_t> class Fixed>
class pool_alloc {
class fixed_alloc_policy {
public:
static void clear() {
ipc::detail::static_for<32>([](auto index) {
Fixed<(decltype(index)::value + 1) * base_size>::pool().clear();
Fixed<(decltype(index)::value + 1) * base_size>::instance().clear();
});
}

10
src/memory/resource.h Executable file → Normal file
View File

@ -19,15 +19,11 @@ namespace ipc {
namespace mem {
template <std::size_t Size>
using sync_fixed_alloc = mem::synchronized<page_fixed_alloc<Size>>;
template <std::size_t Size>
using sync_fixed = mem::detail::fixed<Size, sync_fixed_alloc>;
using sync_pool_alloc = mem::detail::pool_alloc<sync_fixed>;
using static_async_fixed = mem::static_wrapper<mem::async_wrapper<mem::fixed_alloc<Size>>>;
using async_pool_alloc = mem::detail::fixed_alloc_policy<static_async_fixed>;
template <typename T>
using allocator = allocator_wrapper<T, sync_pool_alloc>;
using allocator = allocator_wrapper<T, async_pool_alloc>;
} // namespace mem

View File

@ -3,7 +3,7 @@
#include <limits>
#include <new>
#include <tuple>
#include <map>
#include <thread>
#include <vector>
#include <functional>
#include <utility>
@ -128,76 +128,62 @@ constexpr bool operator!=(const allocator_wrapper<T, AllocP>&, const allocator_w
////////////////////////////////////////////////////////////////
template <typename AllocP>
class synchronized {
class async_wrapper {
public:
using alloc_policy = AllocP;
private:
spin_lock lc_;
std::multimap<std::size_t, alloc_policy*> allocs_;
class alloc_proxy : public alloc_policy {
async_wrapper * w_ = nullptr;
struct alloc_t {
synchronized* t_;
std::size_t s_ = 0;
alloc_policy* a_ = nullptr;
public:
alloc_proxy(alloc_proxy&& rhs)
: alloc_policy(std::move(rhs))
{}
alloc_t(synchronized* t)
: t_ { t } {
{
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(t_->lc_);
auto it = t_->allocs_.begin();
if (it != t_->allocs_.end()) {
std::tie(s_, a_) = *it;
t_->allocs_.erase(it);
}
}
if (a_ == nullptr) {
a_ = new alloc_policy;
alloc_proxy(async_wrapper* w)
: alloc_policy(), w_(w) {
if (w_ == nullptr) return;
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(w_->master_lock_);
if (!w_->master_allocs_.empty()) {
alloc_policy::swap(w_->master_allocs_.back());
w_->master_allocs_.pop_back();
}
}
~alloc_t() {
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(t_->lc_);
t_->allocs_.emplace(s_, a_);
}
void* alloc(std::size_t size) {
void* p = a_->alloc(size);
if ((p != nullptr) && (s_ > 0)) {
--s_;
}
return p;
}
void free(void* p) {
a_->free(p);
++s_;
~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));
}
};
auto& alc_info() {
static tls::pointer<alloc_t> alc;
return *alc.create(this);
friend class alloc_proxy;
spin_lock master_lock_;
std::vector<alloc_proxy> master_allocs_;
auto& get_alloc() {
static tls::pointer<alloc_proxy> tls_alc;
return *tls_alc.create(this);
}
public:
~synchronized() {
~async_wrapper() {
clear();
}
void clear() {
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lc_);
for (auto& pair : allocs_) {
delete pair.second;
}
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
master_allocs_.clear();
}
void* alloc(std::size_t size) {
return alc_info().alloc(size);
return get_alloc().alloc(size);
}
void free(void* p) {
alc_info().free(p);
get_alloc().free(p);
}
void free(void* p, std::size_t /*size*/) {
@ -210,7 +196,7 @@ public:
////////////////////////////////////////////////////////////////
template <typename AllocP>
class statical {
class static_wrapper {
public:
using alloc_policy = AllocP;

View File

@ -8,6 +8,7 @@
#include <atomic>
#include <algorithm>
#include <utility>
#include <new>
#include "def.h"
#include "export.h"
@ -137,5 +138,14 @@ inline void static_for(F&& f) {
static_for(std::make_index_sequence<N>{}, std::forward<F>(f));
}
// Minimum offset between two objects to avoid false sharing.
enum {
// #if __cplusplus >= 201703L
// cache_line_size = std::hardware_destructive_interference_size
// #else /*__cplusplus < 201703L*/
cache_line_size = 64
// #endif/*__cplusplus < 201703L*/
};
} // namespace detail
} // namespace ipc

View File

@ -6,15 +6,15 @@ namespace ipc {
namespace mem {
void pool_alloc::clear() {
sync_pool_alloc::clear();
async_pool_alloc::clear();
}
void* pool_alloc::alloc(std::size_t size) {
return sync_pool_alloc::alloc(size);
return async_pool_alloc::alloc(size);
}
void pool_alloc::free(void* p, std::size_t size) {
sync_pool_alloc::free(p, size);
async_pool_alloc::free(p, size);
}
} // namespace mem

View File

@ -26,8 +26,8 @@ struct prod_cons_impl<wr<relat::single, relat::single, trans::unicast>> {
std::aligned_storage_t<DataSize, AlignSize> data_ {};
};
alignas(circ::cache_line_size) std::atomic<circ::u2_t> rd_; // read index
alignas(circ::cache_line_size) std::atomic<circ::u2_t> wt_; // write index
alignas(detail::cache_line_size) std::atomic<circ::u2_t> rd_; // read index
alignas(detail::cache_line_size) std::atomic<circ::u2_t> wt_; // write index
constexpr circ::u2_t cursor() const noexcept {
return 0;
@ -96,7 +96,7 @@ struct prod_cons_impl<wr<relat::multi , relat::multi, trans::unicast>>
std::atomic<flag_t> f_ct_ { 0 }; // commit flag
};
alignas(circ::cache_line_size) std::atomic<circ::u2_t> ct_; // commit index
alignas(detail::cache_line_size) std::atomic<circ::u2_t> ct_; // commit index
template <typename W, typename F, typename E>
bool push(W* /*wrapper*/, F&& f, E* elems) {
@ -182,7 +182,7 @@ struct prod_cons_impl<wr<relat::single, relat::multi, trans::broadcast>> {
std::atomic<rc_t> rc_ { 0 }; // read-counter
};
alignas(circ::cache_line_size) std::atomic<circ::u2_t> wt_; // write index
alignas(detail::cache_line_size) std::atomic<circ::u2_t> wt_; // write index
circ::u2_t cursor() const noexcept {
return wt_.load(std::memory_order_acquire);
@ -276,7 +276,7 @@ struct prod_cons_impl<wr<relat::multi , relat::multi, trans::broadcast>> {
std::atomic<flag_t> f_ct_ { 0 }; // commit flag
};
alignas(circ::cache_line_size) std::atomic<circ::u2_t> ct_; // commit index
alignas(detail::cache_line_size) std::atomic<circ::u2_t> ct_; // commit index
circ::u2_t cursor() const noexcept {
return ct_.load(std::memory_order_acquire);

View File

@ -238,7 +238,7 @@ private slots:
void test_prod_cons_1v3();
void test_prod_cons_performance();
void test_queue();
} /*unit__*/;
} unit__;
#include "test_circ.moc"

View File

@ -23,14 +23,15 @@ private slots:
void initTestCase();
void test_alloc_free();
void test_linear();
} /*unit__*/;
void test_static();
void test_pool();
} unit__;
#include "test_mem.moc"
constexpr int DataMin = sizeof(void*);
constexpr int DataMax = sizeof(void*) * 16;
constexpr int LoopCount = 1000000;
constexpr int LoopCount = 10000000;
std::vector<std::size_t> sizes__;
@ -106,7 +107,7 @@ void benchmark_alloc() {
void Unit::test_alloc_free() {
benchmark_alloc<ipc::mem::static_alloc>();
benchmark_alloc<ipc::mem::sync_pool_alloc>();
benchmark_alloc<ipc::mem::async_pool_alloc>();
}
template <typename AllocT, typename ModeT, int ThreadsN>
@ -145,7 +146,7 @@ void benchmark_alloc() {
}
}
if ((fini.fetch_add(1, std::memory_order_relaxed) + 1) == ThreadsN) {
sw.print_elapsed<1>(DataMin, DataMax, LoopCount);
sw.print_elapsed<1>(DataMin, DataMax, LoopCount * ThreadsN);
}
}};
++pid;
@ -170,15 +171,16 @@ struct test_performance<AllocT, ModeT, 1> {
}
};
void Unit::test_linear() {
test_performance<ipc::mem::static_alloc, alloc_random, 8>::start();
test_performance<ipc::mem::pool_alloc , alloc_random, 8>::start();
test_performance<ipc::mem::static_alloc, alloc_LIFO , 8>::start();
test_performance<ipc::mem::pool_alloc , alloc_LIFO , 8>::start();
void Unit::test_static() {
test_performance<ipc::mem::static_alloc, alloc_FIFO , 8>::start();
test_performance<ipc::mem::pool_alloc , alloc_FIFO , 8>::start();
test_performance<ipc::mem::static_alloc, alloc_LIFO , 8>::start();
test_performance<ipc::mem::static_alloc, alloc_random, 8>::start();
}
void Unit::test_pool() {
test_performance<ipc::mem::pool_alloc, alloc_FIFO , 8>::start();
test_performance<ipc::mem::pool_alloc, alloc_LIFO , 8>::start();
test_performance<ipc::mem::pool_alloc, alloc_random, 8>::start();
}
} // internal-linkage