mirror of https://github.com/PentHertz/srsLTE.git
Implement a pool in FMT to avoid allocating heap memory when passing a char* to the backend, usually when formatting a %s argument.
Previously since a char* can have any length, this was managed by FMT by converting it into a std::string. Now we store it into a configurable size node that can store a fixed size string, otherwise it falls back to std::string.
This commit is contained in:
parent
b5e879db47
commit
0465f6badd
|
@ -1276,6 +1276,10 @@ template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
|
|||
}
|
||||
|
||||
class dynamic_arg_list {
|
||||
public:
|
||||
static constexpr std::size_t max_pool_string_size = 140;
|
||||
|
||||
private:
|
||||
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
|
||||
// templates it doesn't complain about inability to deduce single translation
|
||||
// unit for placing vtable. So storage_node_base is made a fake template.
|
||||
|
@ -1284,6 +1288,10 @@ class dynamic_arg_list {
|
|||
std::unique_ptr<node<>> next;
|
||||
};
|
||||
|
||||
// Pool storage allocation functions.
|
||||
static void *allocate_from_pool(std::size_t sz);
|
||||
static void free_from_pool(void *ptr);
|
||||
|
||||
template <typename T> struct typed_node : node<> {
|
||||
T value;
|
||||
|
||||
|
@ -1295,9 +1303,35 @@ class dynamic_arg_list {
|
|||
: value(arg.data(), arg.size()) {}
|
||||
};
|
||||
|
||||
struct pooled_node : node<> {
|
||||
std::array<char, max_pool_string_size> value;
|
||||
|
||||
static void* operator new(std::size_t sz) {
|
||||
return allocate_from_pool(sz);
|
||||
}
|
||||
static void operator delete(void* ptr) {
|
||||
free_from_pool(ptr);
|
||||
}
|
||||
|
||||
pooled_node(const char *str, std::size_t sz) {
|
||||
FMT_ASSERT(sz < value.size(), "String is too big");
|
||||
std::copy(str, str + sz, value.begin());
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<node<>> head_;
|
||||
|
||||
public:
|
||||
static constexpr std::size_t max_pool_node_size = sizeof(pooled_node);
|
||||
|
||||
const char *push_small_string(const char *str, std::size_t sz) {
|
||||
auto new_node = std::unique_ptr<pooled_node>(new pooled_node(str, sz));
|
||||
auto& value = new_node->value;
|
||||
new_node->next = std::move(head_);
|
||||
head_ = std::move(new_node);
|
||||
return value.data();
|
||||
}
|
||||
|
||||
template <typename T, typename Arg> const T& push(const Arg& arg) {
|
||||
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
|
||||
auto& value = new_node->value;
|
||||
|
@ -1541,11 +1575,24 @@ class dynamic_format_arg_store
|
|||
std::string result = fmt::vformat("{} and {} and {}", store);
|
||||
\endrst
|
||||
*/
|
||||
template <typename T> void push_back(const T& arg) {
|
||||
if (detail::const_check(need_copy<T>::value))
|
||||
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
|
||||
else
|
||||
template <typename T,
|
||||
typename std::enable_if<detail::is_string<typename std::decay<T>::type>::value, int>::type = 0>
|
||||
void push_back(const T& arg) {
|
||||
fmt::string_view view(arg);
|
||||
if (view.size() + 1 < dynamic_args_.max_pool_string_size) {
|
||||
emplace_arg(dynamic_args_.push_small_string(view.data(), view.size() + 1));
|
||||
} else {
|
||||
emplace_arg(dynamic_args_.push<stored_type<T> >(arg));
|
||||
}
|
||||
}
|
||||
template <typename T,
|
||||
typename std::enable_if<!detail::is_string<typename std::decay<T>::type>::value, int>::type = 0>
|
||||
void push_back(const T& arg) {
|
||||
if (detail::const_check(need_copy<T>::value)) {
|
||||
emplace_arg(dynamic_args_.push<stored_type<T> >(arg));
|
||||
} else {
|
||||
emplace_arg(detail::unwrap(arg));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// For the license information refer to format.h.
|
||||
|
||||
#include "fmt/format-inl.h"
|
||||
#include <mutex>
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
|
@ -23,6 +24,60 @@ int format_float(char* buf, std::size_t size, const char* format, int precision,
|
|||
return precision < 0 ? snprintf_ptr(buf, size, format, value)
|
||||
: snprintf_ptr(buf, size, format, precision, value);
|
||||
}
|
||||
|
||||
#define NODE_POOL_SIZE (10000u)
|
||||
class dyn_node_pool
|
||||
{
|
||||
using type = std::array<uint8_t, dynamic_arg_list::max_pool_node_size>;
|
||||
|
||||
public:
|
||||
dyn_node_pool() {
|
||||
pool.resize(NODE_POOL_SIZE);
|
||||
free_list.reserve(NODE_POOL_SIZE);
|
||||
for (auto& elem : pool) {
|
||||
free_list.push_back(elem.data());
|
||||
}
|
||||
}
|
||||
|
||||
void* alloc(std::size_t sz) {
|
||||
assert(sz <= dynamic_arg_list::max_pool_node_size && "Object is too large to fit in the pool");
|
||||
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
if (free_list.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* p = free_list.back();
|
||||
free_list.pop_back();
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void dealloc(void* p) {
|
||||
if (!p) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
free_list.push_back(reinterpret_cast<uint8_t *>(p));
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<type> pool;
|
||||
std::vector<uint8_t *> free_list;
|
||||
mutable std::mutex m;
|
||||
};
|
||||
|
||||
static dyn_node_pool node_pool;
|
||||
|
||||
void *dynamic_arg_list::allocate_from_pool(std::size_t sz) {
|
||||
return node_pool.alloc(sz);
|
||||
}
|
||||
|
||||
void dynamic_arg_list::free_from_pool(void *ptr) {
|
||||
return node_pool.dealloc(ptr);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
|
||||
|
|
Loading…
Reference in New Issue