include/core/CMemoryDec.h (186 lines of code) (raw):

/* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the following additional limitation. Functionality enabled by the * files subject to the Elastic License 2.0 may only be used in production when * invoked by an Elasticsearch process with a license key installed that permits * use of machine learning features. You may not use this file except in * compliance with the Elastic License 2.0 and the foregoing additional * limitation. */ #ifndef INCLUDED_ml_core_CMemoryDec_h #define INCLUDED_ml_core_CMemoryDec_h #include <core/CMemoryFwd.h> #include <boost/circular_buffer_fwd.hpp> #include <boost/container/container_fwd.hpp> #include <boost/multi_index_container_fwd.hpp> #include <boost/unordered/unordered_map_fwd.hpp> #include <boost/unordered/unordered_set_fwd.hpp> #include <any> #include <array> #include <cstddef> #include <functional> #include <memory> #include <string> #include <type_traits> #include <vector> namespace ml { namespace core { template<typename T, std::size_t N> class CSmallVector; namespace memory_detail { //! \brief Default template for classes that don't sport a staticSize member. template<typename T, typename ENABLE = void> struct SMemoryStaticSize { static std::size_t dispatch(const T& /*t*/) { return sizeof(T); } }; //! \brief Template specialisation for classes having a staticSize member: //! used when base class pointers are passed to dynamicSize(). // clang-format off template<typename T> struct SMemoryStaticSize<T, std::enable_if_t< std::is_same_v<decltype(&T::staticSize), std::size_t (T::*)() const>>> { static std::size_t dispatch(const T& t) { return t.staticSize(); } }; // clang-format on } //! \brief Core memory usage template class. //! //! DESCRIPTION:\n //! Core memory usage template class. Provides a method for determining //! the memory used by different ml classes and standard containers. //! //! ML classes can declare a public member function: //! \code{.cpp} //! std::size_t memoryUsage() const; //! \endcode //! which should call memory::dynamicSize(t); on all its dynamic members. //! //! For virtual hierarchies, the compiler can not determine the size //! of derived classes from the base pointer, so wherever the afore- //! mentioned memoryUsage() function is virtual, an associated function //! \code{.cpp} //! std::size_t staticSize() const; //! \endcode //! should be declared, returning sizeof(*this). namespace memory { //! Default implementation. template<typename T> std::size_t bucketGroupOverhead(const T&) { return 0; } //! The overhead associated with the unordered container's \em bucket_groups. //! On average this is 4 bits per bucket. This is because the bucket_group //! structure consists of a 64 bit bitmask representing up to 64 buckets, //! plus 3 pointers, so a 32 byte structure stores the information for up to //! 64 buckets. //! See https://www.boost.org/doc/libs/1_83_0/libs/unordered/doc/html/unordered.html#structures_closed_addressing_containers //! for more detail. //! \note { In practice we round up the bucket overhead to the next highest multiple of \em 4 * std::size_t} template<typename K, typename V, typename H, typename P, typename A> constexpr std::size_t bucketGroupOverhead(const boost::unordered_map<K, V, H, P, A>& t) { return ((t.bucket_count() + 2 * 4 * sizeof(std::size_t) - 1) / (2 * 4 * sizeof(std::size_t))) * 4 * sizeof(std::size_t); } //! The overhead associated with the unordered container's \em bucket_groups. //! On average this is 4 bits per bucket. This is because the bucket_group //! structure consists of a 64 bit bitmask representing up to 64 buckets, //! plus 3 pointers, so a 32 byte structure stores the information for up to //! 64 buckets.//! See https://www.boost.org/doc/libs/1_83_0/libs/unordered/doc/html/unordered.html#structures_closed_addressing_containers //! for more detail. //! \note { In practice we round up the bucket overhead to the next highest multiple of \em 4 * std::size_t} template<typename T, typename H, typename P, typename A> constexpr std::size_t bucketGroupOverhead(const boost::unordered_set<T, H, P, A>& t) { return ((t.bucket_count() + 2 * 4 * sizeof(std::size_t) - 1) / (2 * 4 * sizeof(std::size_t))) * 4 * sizeof(std::size_t); } //! Default implementation. template<typename T> std::size_t staticSize(const T& t) { return memory_detail::SMemoryStaticSize<T>::dispatch(t); } //! Default implementation. template<typename T> constexpr std::size_t storageNodeOverhead(const T&) { return 0; } //! In Boost 1.83 the bucket lists and node lists are singly linked, hence //! only 1 pointer is needed here template<typename K, typename V, typename H, typename P, typename A> constexpr std::size_t storageNodeOverhead(const boost::unordered_map<K, V, H, P, A>&) { return sizeof(std::size_t); } //! In Boost 1.83 the bucket lists and node lists are singly linked, hence //! only 1 pointer is needed here template<typename T, typename H, typename P, typename A> constexpr std::size_t storageNodeOverhead(const boost::unordered_set<T, H, P, A>&) { return sizeof(std::size_t); } //! Default implementation for non-pointer types. template<typename T> std::size_t dynamicSize(const T& t, std::enable_if_t<!std::is_pointer_v<T>>* = nullptr); //! Default implementation for pointer types. template<typename T> std::size_t dynamicSize(const T& t, std::enable_if_t<std::is_pointer_v<T>>* = nullptr); template<typename T, typename DELETER> std::size_t dynamicSize(const std::unique_ptr<T, DELETER>& t); template<typename T> std::size_t dynamicSize(const std::shared_ptr<T>& t); template<typename T, std::size_t N> std::size_t dynamicSize(const std::array<T, N>& t); template<typename T, typename A> std::size_t dynamicSize(const std::vector<T, A>& t); template<typename T, std::size_t N> std::size_t dynamicSize(const CSmallVector<T, N>& t); template<typename K, typename V, typename H, typename P, typename A> std::size_t dynamicSize(const boost::unordered_map<K, V, H, P, A>& t); template<typename K, typename V, typename C, typename A> std::size_t dynamicSize(const boost::container::flat_map<K, V, C, A>& t); template<typename T, typename H, typename P, typename A> std::size_t dynamicSize(const boost::unordered_set<T, H, P, A>& t); template<typename T, typename C, typename A> std::size_t dynamicSize(const boost::container::flat_set<T, C, A>& t); template<typename T, typename A> std::size_t dynamicSize(const boost::circular_buffer<T, A>& t); template<typename T> std::size_t dynamicSize(const std::optional<T>& t); template<typename T> std::size_t dynamicSize(const std::reference_wrapper<T>& /*t*/); template<typename T, typename V> std::size_t dynamicSize(const std::pair<T, V>& t); CORE_EXPORT std::size_t dynamicSize(const std::string& t); CORE_EXPORT std::size_t dynamicSize(const std::any& t); template<typename T, typename I, typename A> std::size_t dynamicSize(const boost::multi_index::multi_index_container<T, I, A>& t); //! Helper to compute container element dynamic memory usage. template<typename CONTAINER> std::size_t elementDynamicSize(const CONTAINER& t); } //! \brief Core memory debug usage template class. //! //! DESCRIPTION:\n //! Core memory debug usage template class. Provides an extension to the //! CMemory class for creating a detailed breakdown of memory used by //! classes and containers, utilising the CMemoryUsage class. //! //! ML classes can declare a public member function: //! \code{.cpp} //! void debugMemoryUsage(const CMemoryUsage::TMemoryUsagePtr&) const; //! \endcode //! which should call memory_debug::dynamicSize("t_name", t, memUsagePtr) //! on all its dynamic members. namespace memory_debug { //! Default implementation for non-pointer types. template<typename T> void dynamicSize(const char* name, const T& t, const CMemoryUsage::TMemoryUsagePtr& mem, std::enable_if_t<!std::is_pointer_v<T>>* = nullptr); //! Default implementation for pointer types. template<typename T> void dynamicSize(const char* name, const T& t, const CMemoryUsage::TMemoryUsagePtr& mem, std::enable_if_t<std::is_pointer_v<T>>* = nullptr); template<typename T> void dynamicSize(const char* name, const std::unique_ptr<T>& t, const CMemoryUsage::TMemoryUsagePtr& mem); template<typename T> void dynamicSize(const char* name, const std::shared_ptr<T>& t, const CMemoryUsage::TMemoryUsagePtr& mem); template<typename T, std::size_t N> void dynamicSize(const char* name, const std::array<T, N>& t, const CMemoryUsage::TMemoryUsagePtr& mem); template<typename T, typename A> void dynamicSize(const char* name, const std::vector<T, A>& t, const CMemoryUsage::TMemoryUsagePtr& mem); template<typename T, std::size_t N> void dynamicSize(const char* name, const CSmallVector<T, N>& t, const CMemoryUsage::TMemoryUsagePtr& mem); template<typename K, typename V, typename H, typename P, typename A> void dynamicSize(const char* name, const boost::unordered_map<K, V, H, P, A>& t, const CMemoryUsage::TMemoryUsagePtr& mem); template<typename K, typename V, typename C, typename A> void dynamicSize(const char* name, const boost::container::flat_map<K, V, C, A>& t, const CMemoryUsage::TMemoryUsagePtr& mem); template<typename T, typename H, typename P, typename A> void dynamicSize(const char* name, const boost::unordered_set<T, H, P, A>& t, const CMemoryUsage::TMemoryUsagePtr& mem); template<typename T, typename C, typename A> void dynamicSize(const char* name, const boost::container::flat_set<T, C, A>& t, const CMemoryUsage::TMemoryUsagePtr& mem); template<typename T, typename A> void dynamicSize(const char* name, const boost::circular_buffer<T, A>& t, const CMemoryUsage::TMemoryUsagePtr& mem); template<typename T> void dynamicSize(const char* name, const std::optional<T>& t, const CMemoryUsage::TMemoryUsagePtr& mem); template<typename T> void dynamicSize(const char* /*name*/, const std::reference_wrapper<T>& /*t*/, const CMemoryUsage::TMemoryUsagePtr& /*mem*/); template<typename U, typename V> void dynamicSize(const char* name, const std::pair<U, V>& t, const CMemoryUsage::TMemoryUsagePtr& mem); CORE_EXPORT void dynamicSize(const char* name, const std::string& t, const CMemoryUsage::TMemoryUsagePtr& mem); CORE_EXPORT void dynamicSize(const char* name, const std::any& t, const CMemoryUsage::TMemoryUsagePtr& mem); template<typename T, typename I, typename A> void dynamicSize(const char* name, const boost::multi_index::multi_index_container<T, I, A>& t, const CMemoryUsage::TMemoryUsagePtr& mem); //! Helper to debug container element dynamic memory usage. template<typename CONTAINER> void elementDynamicSize(std::string name, const CONTAINER& t, const CMemoryUsage::TMemoryUsagePtr& mem); //! Helper to debug associative container element dynamic memory usage. template<typename CONTAINER> void associativeElementDynamicSize(std::string name, const CONTAINER& t, const CMemoryUsage::TMemoryUsagePtr& mem); } } } #endif // INCLUDED_ml_core_CMemoryDec_h