mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-07 01:06:45 +08:00
optimize the memory allocator
This commit is contained in:
parent
659989fd31
commit
56484c0c8f
3
include/pool_alloc.h
Normal file → Executable file
3
include/pool_alloc.h
Normal file → Executable file
@ -11,9 +11,8 @@ namespace mem {
|
|||||||
|
|
||||||
class IPC_EXPORT pool_alloc {
|
class IPC_EXPORT pool_alloc {
|
||||||
public:
|
public:
|
||||||
static void clear();
|
|
||||||
static void* alloc(std::size_t size);
|
static void* alloc(std::size_t size);
|
||||||
static void free(void* p, std::size_t size);
|
static void free (void* p, std::size_t size);
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
BIN
performance.xlsx
BIN
performance.xlsx
Binary file not shown.
160
src/memory/alloc.h
Normal file → Executable file
160
src/memory/alloc.h
Normal file → Executable file
@ -12,13 +12,14 @@
|
|||||||
|
|
||||||
#include "platform/detail.h"
|
#include "platform/detail.h"
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace mem {
|
namespace mem {
|
||||||
|
|
||||||
class static_alloc {
|
class static_alloc {
|
||||||
public:
|
public:
|
||||||
static void swap(static_alloc&) {}
|
static void swap(static_alloc&) {}
|
||||||
static void clear() {}
|
|
||||||
|
|
||||||
static void* alloc(std::size_t size) {
|
static void* alloc(std::size_t size) {
|
||||||
return size ? std::malloc(size) : nullptr;
|
return size ? std::malloc(size) : nullptr;
|
||||||
@ -104,16 +105,11 @@ private:
|
|||||||
public:
|
public:
|
||||||
scope_alloc() = default;
|
scope_alloc() = default;
|
||||||
|
|
||||||
scope_alloc(scope_alloc&& rhs) { swap(rhs); }
|
scope_alloc(scope_alloc && rhs) { swap(rhs); }
|
||||||
scope_alloc& operator=(scope_alloc rhs) { swap(rhs); return (*this); }
|
scope_alloc& operator=(scope_alloc rhs) { swap(rhs); return (*this); }
|
||||||
|
|
||||||
~scope_alloc() { free_all(); }
|
~scope_alloc() { free_all(); }
|
||||||
|
|
||||||
template <typename A>
|
|
||||||
void set_allocator(A && alc) {
|
|
||||||
alloc_ = std::forward<A>(alc);
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap(scope_alloc& rhs) {
|
void swap(scope_alloc& rhs) {
|
||||||
alloc_.swap(rhs.alloc_);
|
alloc_.swap(rhs.alloc_);
|
||||||
base_t::swap(rhs);
|
base_t::swap(rhs);
|
||||||
@ -130,12 +126,6 @@ public:
|
|||||||
base_t::take(std::move(rhs));
|
base_t::take(std::move(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() {
|
|
||||||
free_all();
|
|
||||||
tail_ = nullptr;
|
|
||||||
alloc_.~alloc_policy();
|
|
||||||
}
|
|
||||||
|
|
||||||
void* alloc(std::size_t size) {
|
void* alloc(std::size_t size) {
|
||||||
auto curr = static_cast<block_t*>(alloc_.alloc(size += aligned_block_size));
|
auto curr = static_cast<block_t*>(alloc_.alloc(size += aligned_block_size));
|
||||||
curr->next_ = head_;
|
curr->next_ = head_;
|
||||||
@ -156,10 +146,12 @@ namespace detail {
|
|||||||
|
|
||||||
class fixed_alloc_base {
|
class fixed_alloc_base {
|
||||||
protected:
|
protected:
|
||||||
|
std::size_t block_size_;
|
||||||
std::size_t init_expand_;
|
std::size_t init_expand_;
|
||||||
void * cursor_;
|
void * cursor_;
|
||||||
|
|
||||||
void init(std::size_t init_expand) {
|
void init(std::size_t block_size, std::size_t init_expand) {
|
||||||
|
block_size_ = block_size;
|
||||||
init_expand_ = init_expand;
|
init_expand_ = init_expand;
|
||||||
cursor_ = nullptr;
|
cursor_ = nullptr;
|
||||||
}
|
}
|
||||||
@ -173,7 +165,12 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
void set_block_size(std::size_t block_size) {
|
||||||
|
block_size_ = block_size;
|
||||||
|
}
|
||||||
|
|
||||||
void swap(fixed_alloc_base& rhs) {
|
void swap(fixed_alloc_base& rhs) {
|
||||||
|
std::swap(block_size_ , rhs.block_size_);
|
||||||
std::swap(init_expand_, rhs.init_expand_);
|
std::swap(init_expand_, rhs.init_expand_);
|
||||||
std::swap(cursor_ , rhs.cursor_);
|
std::swap(cursor_ , rhs.cursor_);
|
||||||
}
|
}
|
||||||
@ -211,6 +208,63 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename AllocP, typename ExpandP>
|
||||||
|
class fixed_alloc : public detail::fixed_alloc_base {
|
||||||
|
public:
|
||||||
|
using base_t = detail::fixed_alloc_base;
|
||||||
|
using alloc_policy = AllocP;
|
||||||
|
|
||||||
|
private:
|
||||||
|
alloc_policy alloc_;
|
||||||
|
|
||||||
|
void* try_expand() {
|
||||||
|
if (empty()) {
|
||||||
|
auto size = ExpandP::next(block_size_, init_expand_);
|
||||||
|
auto p = node_p(cursor_ = alloc_.alloc(size));
|
||||||
|
for (std::size_t i = 0; i < (size / block_size_) - 1; ++i)
|
||||||
|
p = node_p((*p) = reinterpret_cast<byte_t*>(p) + block_size_);
|
||||||
|
(*p) = nullptr;
|
||||||
|
}
|
||||||
|
return cursor_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit fixed_alloc(std::size_t block_size, std::size_t init_expand = 1) {
|
||||||
|
init(block_size, init_expand);
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed_alloc(fixed_alloc && rhs) {
|
||||||
|
init(0, 0);
|
||||||
|
swap(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed_alloc& operator=(fixed_alloc rhs) {
|
||||||
|
swap(rhs);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(fixed_alloc& rhs) {
|
||||||
|
alloc_.swap(rhs.alloc_);
|
||||||
|
base_t::swap(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A = AllocP>
|
||||||
|
auto take(fixed_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
|
||||||
|
base_t::take(std::move(rhs));
|
||||||
|
alloc_.take(std::move(rhs.alloc_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void* alloc() {
|
||||||
|
void* p = try_expand();
|
||||||
|
cursor_ = next(p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* alloc(std::size_t) {
|
||||||
|
return alloc();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
struct fixed_expand_policy {
|
struct fixed_expand_policy {
|
||||||
@ -227,9 +281,8 @@ struct fixed_expand_policy {
|
|||||||
return e * 2;
|
return e * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t BlockSize>
|
static std::size_t next(std::size_t block_size, std::size_t & e) {
|
||||||
static std::size_t next(std::size_t & e) {
|
auto n = ipc::detail::max<std::size_t>(block_size, base_size) * e;
|
||||||
auto n = ipc::detail::max<std::size_t>(BlockSize, base_size) * e;
|
|
||||||
e = next(e);
|
e = next(e);
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
@ -238,73 +291,31 @@ struct fixed_expand_policy {
|
|||||||
template <std::size_t BlockSize,
|
template <std::size_t BlockSize,
|
||||||
typename AllocP = scope_alloc<>,
|
typename AllocP = scope_alloc<>,
|
||||||
typename ExpandP = fixed_expand_policy>
|
typename ExpandP = fixed_expand_policy>
|
||||||
class fixed_alloc : public detail::fixed_alloc_base {
|
class fixed_alloc : public detail::fixed_alloc<AllocP, ExpandP> {
|
||||||
public:
|
public:
|
||||||
using base_t = detail::fixed_alloc_base;
|
using base_t = detail::fixed_alloc<AllocP, ExpandP>;
|
||||||
using alloc_policy = AllocP;
|
|
||||||
|
|
||||||
enum : std::size_t {
|
enum : std::size_t {
|
||||||
block_size = (ipc::detail::max)(BlockSize, sizeof(void*))
|
block_size = (ipc::detail::max)(BlockSize, sizeof(void*))
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
|
||||||
alloc_policy alloc_;
|
|
||||||
|
|
||||||
void* try_expand() {
|
|
||||||
if (empty()) {
|
|
||||||
auto size = ExpandP::template next<block_size>(init_expand_);
|
|
||||||
auto p = node_p(cursor_ = alloc_.alloc(size));
|
|
||||||
for (std::size_t i = 0; i < (size / block_size) - 1; ++i)
|
|
||||||
p = node_p((*p) = reinterpret_cast<byte_t*>(p) + block_size);
|
|
||||||
(*p) = nullptr;
|
|
||||||
}
|
|
||||||
return cursor_;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit fixed_alloc(std::size_t init_expand = 1) {
|
explicit fixed_alloc(std::size_t init_expand = 1)
|
||||||
init(init_expand);
|
: base_t(block_size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed_alloc(fixed_alloc&& rhs) : fixed_alloc() { swap(rhs); }
|
fixed_alloc(fixed_alloc && rhs)
|
||||||
fixed_alloc& operator=(fixed_alloc rhs) { swap(rhs); return (*this); }
|
: base_t(std::move(rhs)) {
|
||||||
|
}
|
||||||
|
|
||||||
template <typename A>
|
fixed_alloc& operator=(fixed_alloc rhs) {
|
||||||
void set_allocator(A && alc) {
|
swap(rhs);
|
||||||
alloc_ = std::forward<A>(alc);
|
return (*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void swap(fixed_alloc& rhs) {
|
void swap(fixed_alloc& rhs) {
|
||||||
alloc_.swap(rhs.alloc_);
|
|
||||||
base_t::swap(rhs);
|
base_t::swap(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename A = AllocP>
|
|
||||||
auto take(fixed_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
|
|
||||||
base_t::take(std::move(rhs));
|
|
||||||
alloc_.take(std::move(rhs.alloc_));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename A = AllocP>
|
|
||||||
auto take(fixed_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> {
|
|
||||||
base_t::take(std::move(rhs));
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() {
|
|
||||||
init_expand_ = ExpandP::prev(init_expand_);
|
|
||||||
cursor_ = nullptr;
|
|
||||||
alloc_.~alloc_policy();
|
|
||||||
}
|
|
||||||
|
|
||||||
void* alloc() {
|
|
||||||
void* p = try_expand();
|
|
||||||
cursor_ = next(p);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* alloc(std::size_t) {
|
|
||||||
return alloc();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
@ -374,14 +385,9 @@ private:
|
|||||||
public:
|
public:
|
||||||
variable_alloc() = default;
|
variable_alloc() = default;
|
||||||
|
|
||||||
variable_alloc(variable_alloc&& rhs) { swap(rhs); }
|
variable_alloc(variable_alloc && rhs) { swap(rhs); }
|
||||||
variable_alloc& operator=(variable_alloc rhs) { swap(rhs); return (*this); }
|
variable_alloc& operator=(variable_alloc rhs) { swap(rhs); return (*this); }
|
||||||
|
|
||||||
template <typename A>
|
|
||||||
void set_allocator(A && alc) {
|
|
||||||
alloc_ = std::forward<A>(alc);
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap(variable_alloc& rhs) {
|
void swap(variable_alloc& rhs) {
|
||||||
alloc_.swap(rhs.alloc_);
|
alloc_.swap(rhs.alloc_);
|
||||||
base_t::swap(rhs);
|
base_t::swap(rhs);
|
||||||
@ -398,10 +404,6 @@ public:
|
|||||||
base_t::take(std::move(rhs));
|
base_t::take(std::move(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() {
|
|
||||||
alloc_.~alloc_policy();
|
|
||||||
}
|
|
||||||
|
|
||||||
void* alloc(std::size_t size) {
|
void* alloc(std::size_t size) {
|
||||||
if (size >= ChunkSize) {
|
if (size >= ChunkSize) {
|
||||||
return alloc_.alloc(size);
|
return alloc_.alloc(size);
|
||||||
|
|||||||
16
src/memory/resource.h
Normal file → Executable file
16
src/memory/resource.h
Normal file → Executable file
@ -21,19 +21,9 @@ using chunk_variable_alloc =
|
|||||||
static_wrapper<async_wrapper<variable_alloc<
|
static_wrapper<async_wrapper<variable_alloc<
|
||||||
sizeof(void*) * 1024 * 256 /* 2MB(x64) */ >>>;
|
sizeof(void*) * 1024 * 256 /* 2MB(x64) */ >>>;
|
||||||
|
|
||||||
template <std::size_t Size>
|
using async_pool_alloc =
|
||||||
using static_async_fixed =
|
static_variable_wrapper<async_wrapper<detail::fixed_alloc<
|
||||||
static_wrapper<async_wrapper<fixed_alloc<
|
chunk_variable_alloc, fixed_expand_policy>>>;
|
||||||
Size, chunk_variable_alloc >>>;
|
|
||||||
|
|
||||||
using big_size_alloc = variable_wrapper<static_async_fixed,
|
|
||||||
default_mapping_policy<
|
|
||||||
default_mapping_policy<>::block_size(default_mapping_policy<>::classes_size),
|
|
||||||
default_mapping_policy<>::iter_size * 2 >>;
|
|
||||||
|
|
||||||
using async_pool_alloc = variable_wrapper<static_async_fixed,
|
|
||||||
default_mapping_policy<>,
|
|
||||||
big_size_alloc>;
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using allocator = allocator_wrapper<T, async_pool_alloc>;
|
using allocator = allocator_wrapper<T, async_pool_alloc>;
|
||||||
|
|||||||
175
src/memory/wrapper.h
Normal file → Executable file
175
src/memory/wrapper.h
Normal file → Executable file
@ -1,14 +1,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <new>
|
#include <new> // ::new
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <functional>
|
#include <functional> // std::function
|
||||||
#include <utility>
|
#include <utility> // std::forward
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <type_traits>
|
#include <cassert> // assert
|
||||||
|
#include <type_traits> // std::aligned_storage_t
|
||||||
|
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
#include "rw_lock.h"
|
#include "rw_lock.h"
|
||||||
@ -80,27 +81,27 @@ public:
|
|||||||
struct rebind { typedef allocator_wrapper<U, AllocP> other; };
|
struct rebind { typedef allocator_wrapper<U, AllocP> other; };
|
||||||
|
|
||||||
constexpr size_type max_size(void) const noexcept {
|
constexpr size_type max_size(void) const noexcept {
|
||||||
return (std::numeric_limits<size_type>::max)() / sizeof(T);
|
return (std::numeric_limits<size_type>::max)() / sizeof(value_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
pointer allocate(size_type count) noexcept {
|
pointer allocate(size_type count) noexcept {
|
||||||
if (count == 0) return nullptr;
|
if (count == 0) return nullptr;
|
||||||
if (count > this->max_size()) return nullptr;
|
if (count > this->max_size()) return nullptr;
|
||||||
return static_cast<pointer>(alloc_.alloc(count * sizeof(T)));
|
return static_cast<pointer>(alloc_.alloc(count * sizeof(value_type)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void deallocate(pointer p, size_type count) noexcept {
|
void deallocate(pointer p, size_type count) noexcept {
|
||||||
alloc_.free(p, count * sizeof(T));
|
alloc_.free(p, count * sizeof(value_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... P>
|
template <typename... P>
|
||||||
static void construct(pointer p, P&&... params) {
|
static void construct(pointer p, P && ... params) {
|
||||||
::new (static_cast<void*>(p)) T(std::forward<P>(params)...);
|
::new (static_cast<void*>(p)) value_type(std::forward<P>(params) ...);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void destroy(pointer p) {
|
static void destroy(pointer p) {
|
||||||
p->~T();
|
p->~value_type();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -148,11 +149,6 @@ public:
|
|||||||
master_allocs_.swap(rhs.master_allocs_);
|
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) {
|
void try_recover(alloc_policy & alc) {
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
||||||
if (!master_allocs_.empty()) {
|
if (!master_allocs_.empty()) {
|
||||||
@ -174,18 +170,16 @@ public:
|
|||||||
|
|
||||||
template <typename A = AllocP>
|
template <typename A = AllocP>
|
||||||
auto try_replenish(alloc_policy & alc, std::size_t /*size*/)
|
auto try_replenish(alloc_policy & alc, std::size_t /*size*/)
|
||||||
-> ipc::require<detail::has_take<A>::value && !has_remain<A>::value && has_empty<A>::value> {
|
-> ipc::require<(!detail::has_take<A>::value || !has_remain<A>::value) && has_empty<A>::value> {
|
||||||
if (!alc.empty()) return;
|
if (!alc.empty()) return;
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
try_recover(alc);
|
||||||
if (!master_allocs_.empty()) {
|
|
||||||
alc.take(std::move(master_allocs_.back()));
|
|
||||||
master_allocs_.pop_back();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename A = AllocP>
|
template <typename A = AllocP>
|
||||||
constexpr auto try_replenish(alloc_policy & /*alc*/, std::size_t /*size*/) const noexcept
|
constexpr auto try_replenish(alloc_policy & /*alc*/, std::size_t /*size*/) const noexcept
|
||||||
-> ipc::require<!detail::has_take<A>::value || (!has_remain<A>::value && !has_empty<A>::value)> {}
|
-> ipc::require<(!detail::has_take<A>::value || !has_remain<A>::value) && !has_empty<A>::value> {
|
||||||
|
// Do Nothing.
|
||||||
|
}
|
||||||
|
|
||||||
void collect(alloc_policy && alc) {
|
void collect(alloc_policy && alc) {
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
||||||
@ -199,7 +193,6 @@ public:
|
|||||||
using alloc_policy = AllocP;
|
using alloc_policy = AllocP;
|
||||||
|
|
||||||
constexpr static void swap(empty_alloc_recycler&) noexcept {}
|
constexpr static void swap(empty_alloc_recycler&) noexcept {}
|
||||||
constexpr static void clear() noexcept {}
|
|
||||||
constexpr static void try_recover(alloc_policy&) noexcept {}
|
constexpr static void try_recover(alloc_policy&) noexcept {}
|
||||||
constexpr static auto try_replenish(alloc_policy&, std::size_t) noexcept {}
|
constexpr static auto try_replenish(alloc_policy&, std::size_t) noexcept {}
|
||||||
constexpr static void collect(alloc_policy&&) noexcept {}
|
constexpr static void collect(alloc_policy&&) noexcept {}
|
||||||
@ -218,51 +211,47 @@ private:
|
|||||||
async_wrapper * w_ = nullptr;
|
async_wrapper * w_ = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
alloc_proxy(alloc_proxy && rhs)
|
alloc_proxy(alloc_proxy && rhs) = default;
|
||||||
: AllocP(std::move(rhs))
|
|
||||||
{}
|
|
||||||
|
|
||||||
alloc_proxy(async_wrapper* w)
|
template <typename ... P>
|
||||||
: AllocP(), w_(w) {
|
alloc_proxy(async_wrapper* w, P && ... pars)
|
||||||
if (w_ == nullptr) return;
|
: AllocP(std::forward<P>(pars) ...), w_(w) {
|
||||||
|
assert(w_ != nullptr);
|
||||||
w_->recycler_.try_recover(*this);
|
w_->recycler_.try_recover(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
~alloc_proxy() {
|
~alloc_proxy() {
|
||||||
if (w_ == nullptr) return;
|
|
||||||
w_->recycler_.collect(std::move(*this));
|
w_->recycler_.collect(std::move(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto alloc(std::size_t size) {
|
// auto alloc(std::size_t size) {
|
||||||
if (w_ != nullptr) {
|
// w_->recycler_.try_replenish(*this, size);
|
||||||
w_->recycler_.try_replenish(*this, size);
|
// return AllocP::alloc(size);
|
||||||
}
|
// }
|
||||||
return AllocP::alloc(size);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
friend class alloc_proxy;
|
friend class alloc_proxy;
|
||||||
|
|
||||||
auto& get_alloc() {
|
using ref_t = alloc_proxy&;
|
||||||
static tls::pointer<alloc_proxy> tls_alc;
|
using tls_t = tls::pointer<alloc_proxy>;
|
||||||
return *tls_alc.create(this);
|
|
||||||
}
|
tls_t tls_;
|
||||||
|
std::function<ref_t()> get_alloc_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void swap(async_wrapper& rhs) {
|
template <typename ... P>
|
||||||
recycler_.swap(rhs.recycler_);
|
async_wrapper(P ... pars) {
|
||||||
}
|
get_alloc_ = [this, pars ...]()->ref_t {
|
||||||
|
return *tls_.create(this, pars ...);
|
||||||
void clear() {
|
};
|
||||||
recycler_.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void* alloc(std::size_t size) {
|
void* alloc(std::size_t size) {
|
||||||
return get_alloc().alloc(size);
|
return get_alloc_().alloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void free(void* p, std::size_t size) {
|
void free(void* p, std::size_t size) {
|
||||||
get_alloc().free(p, size);
|
get_alloc_().free(p, size);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -281,16 +270,16 @@ private:
|
|||||||
alloc_policy alloc_;
|
alloc_policy alloc_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
template <typename ... P>
|
||||||
|
sync_wrapper(P && ... pars)
|
||||||
|
: alloc_(std::forward<P>(pars) ...)
|
||||||
|
{}
|
||||||
|
|
||||||
void swap(sync_wrapper& rhs) {
|
void swap(sync_wrapper& rhs) {
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
alloc_.swap(rhs.alloc_);
|
alloc_.swap(rhs.alloc_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() {
|
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
|
||||||
alloc_.~alloc_policy();
|
|
||||||
}
|
|
||||||
|
|
||||||
void* alloc(std::size_t size) {
|
void* alloc(std::size_t size) {
|
||||||
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
return alloc_.alloc(size);
|
return alloc_.alloc(size);
|
||||||
@ -318,10 +307,6 @@ public:
|
|||||||
|
|
||||||
static void swap(static_wrapper&) {}
|
static void swap(static_wrapper&) {}
|
||||||
|
|
||||||
static void clear() {
|
|
||||||
instance().clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void* alloc(std::size_t size) {
|
static void* alloc(std::size_t size) {
|
||||||
return instance().alloc(size);
|
return instance().alloc(size);
|
||||||
}
|
}
|
||||||
@ -341,62 +326,56 @@ struct default_mapping_policy {
|
|||||||
enum : std::size_t {
|
enum : std::size_t {
|
||||||
base_size = BaseSize,
|
base_size = BaseSize,
|
||||||
iter_size = IterSize,
|
iter_size = IterSize,
|
||||||
classes_size = 32
|
classes_size = 64
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::size_t table[classes_size];
|
template <typename F, typename ... P>
|
||||||
|
IPC_CONSTEXPR_ static void foreach(F f, P ... params) {
|
||||||
IPC_CONSTEXPR_ static std::size_t classify(std::size_t size) noexcept {
|
for (std::size_t i = 0; i < classes_size; ++i) f(i, params...);
|
||||||
auto index = (size <= base_size) ? 0 : ((size - base_size - 1) / iter_size);
|
|
||||||
return (index < classes_size) ?
|
|
||||||
// always uses default_mapping_policy<>::table
|
|
||||||
default_mapping_policy<>::table[index] : classes_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr static std::size_t block_size(std::size_t value) noexcept {
|
IPC_CONSTEXPR_ static std::size_t block_size(std::size_t id) noexcept {
|
||||||
return base_size + (value + 1) * iter_size;
|
return (id < classes_size) ? (base_size + (id + 1) * iter_size) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F, typename D, typename ... P>
|
||||||
|
IPC_CONSTEXPR_ static auto classify(F f, D d, std::size_t size, P ... params) {
|
||||||
|
std::size_t id = (size - base_size - 1) / iter_size;
|
||||||
|
return (id < classes_size) ? f(id, params..., size) : d(params..., size);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <std::size_t B, std::size_t I>
|
template <typename FixedAlloc,
|
||||||
const std::size_t default_mapping_policy<B, I>::table[default_mapping_policy<B, I>::classes_size] = {
|
typename DefaultAlloc = mem::static_alloc,
|
||||||
/* 1 - 8 ~ 32 */
|
typename MappingP = default_mapping_policy<>>
|
||||||
0 , 1 , 2 , 3 ,
|
class static_variable_wrapper {
|
||||||
/* 2 - 48 ~ 256 */
|
private:
|
||||||
5 , 5 , 7 , 7 , 9 , 9 , 11, 11, 13, 13, 15, 15, 17, 17,
|
static FixedAlloc& instance(std::size_t id) {
|
||||||
19, 19, 21, 21, 23, 23, 25, 25, 27, 27, 29, 29, 31, 31
|
static struct initiator {
|
||||||
};
|
std::aligned_storage_t<sizeof (FixedAlloc),
|
||||||
|
alignof(FixedAlloc)> arr_[MappingP::classes_size];
|
||||||
template <template <std::size_t> class Fixed,
|
initiator() {
|
||||||
typename MappingP = default_mapping_policy<>,
|
MappingP::foreach([](std::size_t id, initiator* t) {
|
||||||
typename StaticAlloc = mem::static_alloc>
|
::new (&(t->arr_[id])) FixedAlloc(MappingP::block_size(id));
|
||||||
class variable_wrapper {
|
}, this);
|
||||||
|
}
|
||||||
template <typename F>
|
} init__;
|
||||||
constexpr static auto choose(std::size_t size, F&& f) {
|
return reinterpret_cast<FixedAlloc&>(init__.arr_[id]);
|
||||||
return ipc::detail::static_switch<MappingP::classes_size>(MappingP::classify(size), [&f](auto index) {
|
|
||||||
return f(Fixed<MappingP::block_size(decltype(index)::value)>{});
|
|
||||||
}, [&f] {
|
|
||||||
return f(StaticAlloc{});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void swap(variable_wrapper&) {}
|
static void swap(static_variable_wrapper&) {}
|
||||||
|
|
||||||
static void clear() {
|
|
||||||
ipc::detail::static_for<MappingP::classes_size>([](auto index) {
|
|
||||||
Fixed<MappingP::block_size(decltype(index)::value)>::clear();
|
|
||||||
});
|
|
||||||
StaticAlloc::clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void* alloc(std::size_t size) {
|
static void* alloc(std::size_t size) {
|
||||||
return choose(size, [size](auto&& alc) { return alc.alloc(size); });
|
return MappingP::classify([](std::size_t id, std::size_t size) {
|
||||||
|
return instance(id).alloc(size);
|
||||||
|
}, DefaultAlloc::alloc, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free(void* p, std::size_t size) {
|
static void free(void* p, std::size_t size) {
|
||||||
choose(size, [p, size](auto&& alc) { alc.free(p, size); });
|
MappingP::classify([](std::size_t id, void* p, std::size_t size) {
|
||||||
|
instance(id).free(p, size);
|
||||||
|
}, static_cast<void(*)(void*, std::size_t)>(DefaultAlloc::free), size, p);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
4
src/pool_alloc.cpp
Normal file → Executable file
4
src/pool_alloc.cpp
Normal file → Executable file
@ -5,10 +5,6 @@
|
|||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace mem {
|
namespace mem {
|
||||||
|
|
||||||
void pool_alloc::clear() {
|
|
||||||
async_pool_alloc::clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void* pool_alloc::alloc(std::size_t size) {
|
void* pool_alloc::alloc(std::size_t size) {
|
||||||
return async_pool_alloc::alloc(size);
|
return async_pool_alloc::alloc(size);
|
||||||
}
|
}
|
||||||
|
|||||||
9
test/test.h
Normal file → Executable file
9
test/test.h
Normal file → Executable file
@ -40,12 +40,11 @@ struct test_stopwatch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int Factor>
|
template <int Factor, typename ToDur = std::chrono::microseconds>
|
||||||
void print_elapsed(int N, int M, int Loops) {
|
void print_elapsed(int N, int M, int Loops, const char * unit = " us/d") {
|
||||||
auto ts = sw_.elapsed<std::chrono::microseconds>();
|
auto ts = sw_.elapsed<ToDur>();
|
||||||
std::cout << "[" << N << ":" << M << ", " << Loops << "] "
|
std::cout << "[" << N << ":" << M << ", " << Loops << "] "
|
||||||
<< "performance: " << (ts / 1000.0) << " ms, "
|
<< (double(ts) / double(Factor ? (Loops * Factor) : (Loops * N))) << unit << std::endl;
|
||||||
<< (double(ts) / double(Factor ? (Loops * Factor) : (Loops * N))) << " us/d" << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_elapsed(int N, int M, int Loops) {
|
void print_elapsed(int N, int M, int Loops) {
|
||||||
|
|||||||
3
test/test_circ.cpp
Normal file → Executable file
3
test/test_circ.cpp
Normal file → Executable file
@ -238,7 +238,8 @@ private slots:
|
|||||||
void test_prod_cons_1v3();
|
void test_prod_cons_1v3();
|
||||||
void test_prod_cons_performance();
|
void test_prod_cons_performance();
|
||||||
void test_queue();
|
void test_queue();
|
||||||
} unit__;
|
};
|
||||||
|
// } unit__;
|
||||||
|
|
||||||
#include "test_circ.moc"
|
#include "test_circ.moc"
|
||||||
|
|
||||||
|
|||||||
8
test/test_ipc.cpp
Normal file → Executable file
8
test/test_ipc.cpp
Normal file → Executable file
@ -30,7 +30,7 @@ std::vector<ipc::buff_t> datas__;
|
|||||||
constexpr int DataMin = 2;
|
constexpr int DataMin = 2;
|
||||||
constexpr int DataMax = 256;
|
constexpr int DataMax = 256;
|
||||||
constexpr int LoopCount = 100000;
|
constexpr int LoopCount = 100000;
|
||||||
//constexpr int LoopCount = 1000;
|
// constexpr int LoopCount = 1000;
|
||||||
|
|
||||||
} // internal-linkage
|
} // internal-linkage
|
||||||
|
|
||||||
@ -179,6 +179,7 @@ private slots:
|
|||||||
void test_channel();
|
void test_channel();
|
||||||
void test_channel_rtt();
|
void test_channel_rtt();
|
||||||
void test_channel_performance();
|
void test_channel_performance();
|
||||||
|
// };
|
||||||
} unit__;
|
} unit__;
|
||||||
|
|
||||||
#include "test_ipc.moc"
|
#include "test_ipc.moc"
|
||||||
@ -352,7 +353,6 @@ void Unit::test_route() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_route_rtt() {
|
void Unit::test_route_rtt() {
|
||||||
//return;
|
|
||||||
test_stopwatch sw;
|
test_stopwatch sw;
|
||||||
|
|
||||||
std::thread t1 {[&] {
|
std::thread t1 {[&] {
|
||||||
@ -392,15 +392,13 @@ void Unit::test_route_rtt() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_route_performance() {
|
void Unit::test_route_performance() {
|
||||||
//return;
|
|
||||||
ipc::detail::static_for<8>([](auto index) {
|
ipc::detail::static_for<8>([](auto index) {
|
||||||
test_prod_cons<ipc::route, 1, decltype(index)::value + 1, false>();
|
test_prod_cons<ipc::route, 1, decltype(index)::value + 1, false>();
|
||||||
});
|
});
|
||||||
test_prod_cons<ipc::route, 1, 8>(); // test & verify
|
// test_prod_cons<ipc::route, 1, 8>(); // test & verify
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::test_channel() {
|
void Unit::test_channel() {
|
||||||
//return;
|
|
||||||
std::thread t1 {[&] {
|
std::thread t1 {[&] {
|
||||||
ipc::channel cc { "my-ipc-channel" };
|
ipc::channel cc { "my-ipc-channel" };
|
||||||
for (std::size_t i = 0;; ++i) {
|
for (std::size_t i = 0;; ++i) {
|
||||||
|
|||||||
224
test/test_mem.cpp
Normal file → Executable file
224
test/test_mem.cpp
Normal file → Executable file
@ -1,4 +1,5 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
@ -8,7 +9,7 @@
|
|||||||
#include "memory/resource.h"
|
#include "memory/resource.h"
|
||||||
#include "pool_alloc.h"
|
#include "pool_alloc.h"
|
||||||
|
|
||||||
//#include "gperftools/tcmalloc.h"
|
// #include "gperftools/tcmalloc.h"
|
||||||
|
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
|
|
||||||
@ -24,58 +25,77 @@ class Unit : public TestSuite {
|
|||||||
private slots:
|
private slots:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
|
|
||||||
void test_alloc_free();
|
void test_static_alloc();
|
||||||
} unit__;
|
void test_pool_alloc();
|
||||||
|
void test_tc_alloc();
|
||||||
|
};
|
||||||
|
// } unit__;
|
||||||
|
|
||||||
#include "test_mem.moc"
|
#include "test_mem.moc"
|
||||||
|
|
||||||
constexpr int DataMin = 4;
|
constexpr int DataMin = 4;
|
||||||
constexpr int DataMax = 256;
|
constexpr int DataMax = 256;
|
||||||
constexpr int LoopCount = 100000;
|
constexpr int LoopCount = 4194304;
|
||||||
|
|
||||||
|
// constexpr int DataMin = 256;
|
||||||
|
// constexpr int DataMax = 512;
|
||||||
|
// constexpr int LoopCount = 2097152;
|
||||||
|
|
||||||
std::vector<std::size_t> sizes__;
|
std::vector<std::size_t> sizes__;
|
||||||
|
|
||||||
template <typename M>
|
template <typename M>
|
||||||
struct alloc_ix_t {
|
struct alloc_ix_t {
|
||||||
static std::vector<int> ix_[2];
|
static std::vector<int> ix_;
|
||||||
static bool inited_;
|
static bool inited_;
|
||||||
|
|
||||||
|
alloc_ix_t() {
|
||||||
|
if (inited_) return;
|
||||||
|
inited_ = true;
|
||||||
|
M::init(ix_);
|
||||||
|
}
|
||||||
|
|
||||||
|
int index(std::size_t /*pid*/, std::size_t /*k*/, std::size_t n) {
|
||||||
|
return ix_[n];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename M>
|
template <typename M>
|
||||||
std::vector<int> alloc_ix_t<M>::ix_[2] = { std::vector<int>(LoopCount), std::vector<int>(LoopCount) };
|
std::vector<int> alloc_ix_t<M>::ix_(LoopCount);
|
||||||
template <typename M>
|
template <typename M>
|
||||||
bool alloc_ix_t<M>::inited_ = false;
|
bool alloc_ix_t<M>::inited_ = false;
|
||||||
|
|
||||||
struct alloc_random : alloc_ix_t<alloc_random> {
|
template <std::size_t N>
|
||||||
alloc_random() {
|
struct alloc_FIFO : alloc_ix_t<alloc_FIFO<N>> {
|
||||||
if (inited_) return;
|
static void init(std::vector<int>& ix) {
|
||||||
inited_ = true;
|
for (int i = 0; i < LoopCount; ++i) {
|
||||||
|
ix[static_cast<std::size_t>(i)] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
struct alloc_LIFO : alloc_ix_t<alloc_LIFO<N>> {
|
||||||
|
static void init(std::vector<int>& ix) {
|
||||||
|
for (int i = 0; i < LoopCount; ++i) {
|
||||||
|
ix[static_cast<std::size_t>(i)] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int index(std::size_t pid, std::size_t k, std::size_t n) {
|
||||||
|
constexpr static int CacheSize = LoopCount / N;
|
||||||
|
if (k) {
|
||||||
|
return this->ix_[(CacheSize * (2 * pid + 1)) - 1 - n];
|
||||||
|
}
|
||||||
|
else return this->ix_[n];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
struct alloc_random : alloc_ix_t<alloc_random<N>> {
|
||||||
|
static void init(std::vector<int>& ix) {
|
||||||
capo::random<> rdm_index(0, LoopCount - 1);
|
capo::random<> rdm_index(0, LoopCount - 1);
|
||||||
for (int i = 0; i < LoopCount; ++i) {
|
for (int i = 0; i < LoopCount; ++i) {
|
||||||
ix_[0][static_cast<std::size_t>(i)] =
|
ix[static_cast<std::size_t>(i)] = rdm_index();
|
||||||
ix_[1][static_cast<std::size_t>(i)] = rdm_index();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct alloc_LIFO : alloc_ix_t<alloc_LIFO> {
|
|
||||||
alloc_LIFO() {
|
|
||||||
if (inited_) return;
|
|
||||||
inited_ = true;
|
|
||||||
for (int i = 0, n = LoopCount - 1; i < LoopCount; ++i, --n) {
|
|
||||||
ix_[0][static_cast<std::size_t>(i)] =
|
|
||||||
ix_[1][static_cast<std::size_t>(n)] = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct alloc_FIFO : alloc_ix_t<alloc_FIFO> {
|
|
||||||
alloc_FIFO() {
|
|
||||||
if (inited_) return;
|
|
||||||
inited_ = true;
|
|
||||||
for (int i = 0; i < LoopCount; ++i) {
|
|
||||||
ix_[0][static_cast<std::size_t>(i)] =
|
|
||||||
ix_[1][static_cast<std::size_t>(i)] = i;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -89,33 +109,13 @@ void Unit::initTestCase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename AllocT>
|
template <typename AllocT, int ThreadsN>
|
||||||
void benchmark_alloc() {
|
|
||||||
std::cout << std::endl << type_name<AllocT>() << std::endl;
|
|
||||||
|
|
||||||
test_stopwatch sw;
|
|
||||||
sw.start();
|
|
||||||
|
|
||||||
for (std::size_t k = 0; k < 100; ++k)
|
|
||||||
for (std::size_t n = 0; n < LoopCount; ++n) {
|
|
||||||
std::size_t s = sizes__[n];
|
|
||||||
AllocT::free(AllocT::alloc(s), s);
|
|
||||||
}
|
|
||||||
|
|
||||||
sw.print_elapsed<1>(DataMin, DataMax, LoopCount * 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename AllocT, typename ModeT, int ThreadsN>
|
|
||||||
void benchmark_alloc() {
|
void benchmark_alloc() {
|
||||||
std::cout << std::endl
|
std::cout << std::endl
|
||||||
<< "[Threads: " << ThreadsN << ", Mode: " << type_name<ModeT>() << "] "
|
<< "[Threads: " << ThreadsN << "] "
|
||||||
<< type_name<AllocT>() << std::endl;
|
<< type_name<AllocT>() << std::endl;
|
||||||
|
|
||||||
std::vector<void*> ptrs[ThreadsN];
|
constexpr static int CacheSize = LoopCount / ThreadsN;
|
||||||
for (auto& vec : ptrs) {
|
|
||||||
vec.resize(LoopCount);
|
|
||||||
}
|
|
||||||
ModeT mode;
|
|
||||||
|
|
||||||
std::atomic_int fini { 0 };
|
std::atomic_int fini { 0 };
|
||||||
test_stopwatch sw;
|
test_stopwatch sw;
|
||||||
@ -126,11 +126,50 @@ void benchmark_alloc() {
|
|||||||
for (auto& w : works) {
|
for (auto& w : works) {
|
||||||
w = std::thread {[&, pid] {
|
w = std::thread {[&, pid] {
|
||||||
sw.start();
|
sw.start();
|
||||||
for (std::size_t k = 0; k < 10; ++k)
|
for (std::size_t k = 0; k < 100; ++k)
|
||||||
for (std::size_t x = 0; x < 2; ++x) {
|
for (std::size_t n = (CacheSize * pid); n < (CacheSize * (pid + 1)); ++n) {
|
||||||
for(std::size_t n = 0; n < LoopCount; ++n) {
|
std::size_t s = sizes__[n];
|
||||||
int m = mode.ix_[x][n];
|
AllocT::free(AllocT::alloc(s), s);
|
||||||
void*& p = ptrs[pid][static_cast<std::size_t>(m)];
|
}
|
||||||
|
if ((fini.fetch_add(1, std::memory_order_relaxed) + 1) == ThreadsN) {
|
||||||
|
sw.print_elapsed<1, std::chrono::nanoseconds>(DataMin, DataMax, LoopCount * 100, " ns/d");
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
++pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& w : works) w.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AllocT, template <std::size_t> class ModeT, int ThreadsN>
|
||||||
|
void benchmark_alloc() {
|
||||||
|
std::cout << std::endl
|
||||||
|
<< "[Threads: " << ThreadsN << ", Mode: " << type_name<ModeT<ThreadsN>>() << "] "
|
||||||
|
<< type_name<AllocT>() << std::endl;
|
||||||
|
|
||||||
|
constexpr static int CacheSize = LoopCount / ThreadsN;
|
||||||
|
|
||||||
|
std::vector<void*> ptrs[ThreadsN];
|
||||||
|
for (auto& vec : ptrs) {
|
||||||
|
vec.resize(LoopCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModeT<ThreadsN> mode;
|
||||||
|
|
||||||
|
std::atomic_int fini { 0 };
|
||||||
|
test_stopwatch sw;
|
||||||
|
|
||||||
|
std::thread works[ThreadsN];
|
||||||
|
int pid = 0;
|
||||||
|
|
||||||
|
for (auto& w : works) {
|
||||||
|
w = std::thread {[&, pid] {
|
||||||
|
auto& vec = ptrs[pid];
|
||||||
|
sw.start();
|
||||||
|
for (std::size_t k = 0; k < 2; ++k)
|
||||||
|
for (std::size_t n = (CacheSize * pid); n < (CacheSize * (pid + 1)); ++n) {
|
||||||
|
int m = mode.index(pid, k, n);
|
||||||
|
void*& p = vec[static_cast<std::size_t>(m)];
|
||||||
std::size_t s = sizes__[static_cast<std::size_t>(m)];
|
std::size_t s = sizes__[static_cast<std::size_t>(m)];
|
||||||
if (p == nullptr) {
|
if (p == nullptr) {
|
||||||
p = AllocT::alloc(s);
|
p = AllocT::alloc(s);
|
||||||
@ -140,35 +179,50 @@ void benchmark_alloc() {
|
|||||||
p = nullptr;
|
p = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if ((fini.fetch_add(1, std::memory_order_relaxed) + 1) == ThreadsN) {
|
if ((fini.fetch_add(1, std::memory_order_relaxed) + 1) == ThreadsN) {
|
||||||
sw.print_elapsed<1>(DataMin, DataMax, LoopCount * 10 * ThreadsN);
|
sw.print_elapsed<1>(DataMin, DataMax, LoopCount);
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
++pid;
|
++pid;
|
||||||
}
|
}
|
||||||
sw.start();
|
|
||||||
|
|
||||||
for (auto& w : works) w.join();
|
for (auto& w : works) w.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename AllocT, typename ModeT, int ThreadsN>
|
template <typename AllocT, template <std::size_t> class ModeT, int ThreadsN>
|
||||||
struct test_performance {
|
struct test_performance {
|
||||||
static void start() {
|
static void start() {
|
||||||
test_performance<AllocT, ModeT, ThreadsN - 1>::start();
|
test_performance<AllocT, ModeT, ThreadsN / 2>::start();
|
||||||
benchmark_alloc<AllocT, ModeT, ThreadsN>();
|
benchmark_alloc<AllocT, ModeT, ThreadsN>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename AllocT, typename ModeT>
|
template <typename AllocT, template <std::size_t> class ModeT>
|
||||||
struct test_performance<AllocT, ModeT, 1> {
|
struct test_performance<AllocT, ModeT, 1> {
|
||||||
static void start() {
|
static void start() {
|
||||||
benchmark_alloc<AllocT, ModeT, 1>();
|
benchmark_alloc<AllocT, ModeT, 1>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//class tc_alloc {
|
template <std::size_t> struct dummy;
|
||||||
//public:
|
|
||||||
|
template <typename AllocT, int ThreadsN>
|
||||||
|
struct test_performance<AllocT, dummy, ThreadsN> {
|
||||||
|
static void start() {
|
||||||
|
test_performance<AllocT, dummy, ThreadsN / 2>::start();
|
||||||
|
benchmark_alloc<AllocT, ThreadsN>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename AllocT>
|
||||||
|
struct test_performance<AllocT, dummy, 1> {
|
||||||
|
static void start() {
|
||||||
|
benchmark_alloc<AllocT, 1>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// class tc_alloc {
|
||||||
|
// public:
|
||||||
// static void clear() {}
|
// static void clear() {}
|
||||||
|
|
||||||
// static void* alloc(std::size_t size) {
|
// static void* alloc(std::size_t size) {
|
||||||
@ -178,18 +232,30 @@ struct test_performance<AllocT, ModeT, 1> {
|
|||||||
// static void free(void* p, std::size_t size) {
|
// static void free(void* p, std::size_t size) {
|
||||||
// tc_free_sized(p, size);
|
// tc_free_sized(p, size);
|
||||||
// }
|
// }
|
||||||
//};
|
// };
|
||||||
|
|
||||||
#define TEST_ALLOC_TYPE /*ipc::mem::static_alloc*/ ipc::mem::async_pool_alloc /*tc_alloc*/
|
void Unit::test_static_alloc() {
|
||||||
|
// test_performance<ipc::mem::static_alloc, dummy , 128>::start();
|
||||||
|
// test_performance<ipc::mem::static_alloc, alloc_FIFO , 128>::start();
|
||||||
|
// test_performance<ipc::mem::static_alloc, alloc_LIFO , 128>::start();
|
||||||
|
// test_performance<ipc::mem::static_alloc, alloc_random, 128>::start();
|
||||||
|
}
|
||||||
|
|
||||||
void Unit::test_alloc_free() {
|
void Unit::test_pool_alloc() {
|
||||||
// benchmark_alloc <TEST_ALLOC_TYPE>();
|
test_performance<ipc::mem::async_pool_alloc, dummy , 128>::start();
|
||||||
// test_performance<TEST_ALLOC_TYPE, alloc_FIFO , 24>::start();
|
test_performance<ipc::mem::async_pool_alloc, alloc_FIFO , 128>::start();
|
||||||
|
|
||||||
benchmark_alloc <TEST_ALLOC_TYPE>();
|
test_performance<ipc::mem::async_pool_alloc, dummy , 128>::start();
|
||||||
test_performance<TEST_ALLOC_TYPE, alloc_FIFO , 16>::start();
|
test_performance<ipc::mem::async_pool_alloc, alloc_FIFO , 128>::start();
|
||||||
test_performance<TEST_ALLOC_TYPE, alloc_LIFO , 16>::start();
|
test_performance<ipc::mem::async_pool_alloc, alloc_LIFO , 128>::start();
|
||||||
test_performance<TEST_ALLOC_TYPE, alloc_random, 16>::start();
|
test_performance<ipc::mem::async_pool_alloc, alloc_random, 128>::start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unit::test_tc_alloc() {
|
||||||
|
// test_performance<tc_alloc, dummy , 128>::start();
|
||||||
|
// test_performance<tc_alloc, alloc_FIFO , 128>::start();
|
||||||
|
// test_performance<tc_alloc, alloc_LIFO , 128>::start();
|
||||||
|
// test_performance<tc_alloc, alloc_random, 128>::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // internal-linkage
|
} // internal-linkage
|
||||||
|
|||||||
0
test/test_shm.cpp
Normal file → Executable file
0
test/test_shm.cpp
Normal file → Executable file
0
test/test_waiter.cpp
Normal file → Executable file
0
test/test_waiter.cpp
Normal file → Executable file
Loading…
x
Reference in New Issue
Block a user