From 0465f6badde29d90eb43701b15dfe01833b68392 Mon Sep 17 00:00:00 2001 From: faluco Date: Thu, 8 Apr 2021 13:59:46 +0200 Subject: [PATCH] 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. --- lib/include/srsran/srslog/bundled/fmt/core.h | 55 ++++++++++++++++++-- lib/src/srslog/bundled/fmt/format.cc | 55 ++++++++++++++++++++ 2 files changed, 106 insertions(+), 4 deletions(-) diff --git a/lib/include/srsran/srslog/bundled/fmt/core.h b/lib/include/srsran/srslog/bundled/fmt/core.h index 252dabbe8..045ec79fa 100644 --- a/lib/include/srsran/srslog/bundled/fmt/core.h +++ b/lib/include/srsran/srslog/bundled/fmt/core.h @@ -1276,6 +1276,10 @@ template const T& unwrap(const std::reference_wrapper& 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> next; }; + // Pool storage allocation functions. + static void *allocate_from_pool(std::size_t sz); + static void free_from_pool(void *ptr); + template 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 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> 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(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 const T& push(const Arg& arg) { auto new_node = std::unique_ptr>(new typed_node(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 void push_back(const T& arg) { - if (detail::const_check(need_copy::value)) - emplace_arg(dynamic_args_.push>(arg)); - else + template ::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 >(arg)); + } + } + template ::type>::value, int>::type = 0> + void push_back(const T& arg) { + if (detail::const_check(need_copy::value)) { + emplace_arg(dynamic_args_.push >(arg)); + } else { emplace_arg(detail::unwrap(arg)); + } } /** diff --git a/lib/src/srslog/bundled/fmt/format.cc b/lib/src/srslog/bundled/fmt/format.cc index a64a1f389..a443544dd 100644 --- a/lib/src/srslog/bundled/fmt/format.cc +++ b/lib/src/srslog/bundled/fmt/format.cc @@ -6,6 +6,7 @@ // For the license information refer to format.h. #include "fmt/format-inl.h" +#include 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; + +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 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 lock(m); + free_list.push_back(reinterpret_cast(p)); + } + +private: + std::vector pool; + std::vector 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;