123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683 |
- // Copyright 2016 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #ifndef BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
- #define BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
- #include <stdint.h>
- #include <array>
- #include <deque>
- #include <list>
- #include <map>
- #include <memory>
- #include <queue>
- #include <set>
- #include <stack>
- #include <string>
- #include <type_traits>
- #include <unordered_map>
- #include <unordered_set>
- #include <vector>
- #include "base/base_export.h"
- #include "base/containers/circular_deque.h"
- #include "base/containers/flat_map.h"
- #include "base/containers/flat_set.h"
- #include "base/containers/linked_list.h"
- #include "base/containers/lru_cache.h"
- #include "base/containers/queue.h"
- #include "base/memory/raw_ptr.h"
- #include "base/stl_util.h"
- #include "base/template_util.h"
- // Composable memory usage estimators.
- //
- // This file defines set of EstimateMemoryUsage(object) functions that return
- // approximate dynamically allocated memory usage of their argument.
- //
- // The ultimate goal is to make memory usage estimation for a class simply a
- // matter of aggregating EstimateMemoryUsage() results over all fields.
- //
- // That is achieved via composability: if EstimateMemoryUsage() is defined
- // for T then EstimateMemoryUsage() is also defined for any combination of
- // containers holding T (e.g. std::map<int, std::vector<T>>).
- //
- // There are two ways of defining EstimateMemoryUsage() for a type:
- //
- // 1. As a global function 'size_t EstimateMemoryUsage(T)' in
- // in base::trace_event namespace.
- //
- // 2. As 'size_t T::EstimateMemoryUsage() const' method. In this case
- // EstimateMemoryUsage(T) function in base::trace_event namespace is
- // provided automatically.
- //
- // Here is an example implementation:
- //
- // class MyClass {
- // ...
- // ...
- // size_t EstimateMemoryUsage() const {
- // return base::trace_event::EstimateMemoryUsage(set_) +
- // base::trace_event::EstimateMemoryUsage(name_) +
- // base::trace_event::EstimateMemoryUsage(foo_);
- // }
- // ...
- // private:
- // ...
- // std::set<int> set_;
- // std::string name_;
- // Foo foo_;
- // int id_;
- // bool success_;
- // }
- //
- // The approach is simple: first call EstimateMemoryUsage() on all members,
- // then recursively fix compilation errors that are caused by types not
- // implementing EstimateMemoryUsage().
- namespace base {
- namespace trace_event {
- // Declarations
- // If T declares 'EstimateMemoryUsage() const' member function, then
- // global function EstimateMemoryUsage(T) is available, and just calls
- // the member function.
- template <class T>
- auto EstimateMemoryUsage(const T& object)
- -> decltype(object.EstimateMemoryUsage());
- // String
- template <class C, class T, class A>
- size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string);
- // Arrays
- template <class T, size_t N>
- size_t EstimateMemoryUsage(const std::array<T, N>& array);
- template <class T, size_t N>
- size_t EstimateMemoryUsage(T (&array)[N]);
- template <class T>
- size_t EstimateMemoryUsage(const T* array, size_t array_length);
- // std::unique_ptr
- template <class T, class D>
- size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr);
- template <class T, class D>
- size_t EstimateMemoryUsage(const std::unique_ptr<T[], D>& array,
- size_t array_length);
- // std::shared_ptr
- template <class T>
- size_t EstimateMemoryUsage(const std::shared_ptr<T>& ptr);
- // Containers
- template <class F, class S>
- size_t EstimateMemoryUsage(const std::pair<F, S>& pair);
- template <class T, class A>
- size_t EstimateMemoryUsage(const std::vector<T, A>& vector);
- template <class T, class A>
- size_t EstimateMemoryUsage(const std::list<T, A>& list);
- template <class T>
- size_t EstimateMemoryUsage(const base::LinkedList<T>& list);
- template <class T, class C, class A>
- size_t EstimateMemoryUsage(const std::set<T, C, A>& set);
- template <class T, class C, class A>
- size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set);
- template <class K, class V, class C, class A>
- size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map);
- template <class K, class V, class C, class A>
- size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map);
- template <class T, class H, class KE, class A>
- size_t EstimateMemoryUsage(const std::unordered_set<T, H, KE, A>& set);
- template <class T, class H, class KE, class A>
- size_t EstimateMemoryUsage(const std::unordered_multiset<T, H, KE, A>& set);
- template <class K, class V, class H, class KE, class A>
- size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map);
- template <class K, class V, class H, class KE, class A>
- size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map);
- template <class T, class A>
- size_t EstimateMemoryUsage(const std::deque<T, A>& deque);
- template <class T, class C>
- size_t EstimateMemoryUsage(const std::queue<T, C>& queue);
- template <class T, class C>
- size_t EstimateMemoryUsage(const std::priority_queue<T, C>& queue);
- template <class T, class C>
- size_t EstimateMemoryUsage(const std::stack<T, C>& stack);
- template <class T>
- size_t EstimateMemoryUsage(const base::circular_deque<T>& deque);
- template <class T, class C>
- size_t EstimateMemoryUsage(const base::flat_set<T, C>& set);
- template <class K, class V, class C>
- size_t EstimateMemoryUsage(const base::flat_map<K, V, C>& map);
- template <class K, class V, class C>
- size_t EstimateMemoryUsage(const base::LRUCache<K, V, C>& lru);
- template <class K, class V, class C>
- size_t EstimateMemoryUsage(const base::HashingLRUCache<K, V, C>& lru);
- template <class V, class C>
- size_t EstimateMemoryUsage(const base::LRUCacheSet<V, C>& lru);
- template <class V, class C>
- size_t EstimateMemoryUsage(const base::HashingLRUCacheSet<V, C>& lru);
- // TODO(dskiba):
- // std::forward_list
- // Definitions
- namespace internal {
- // HasEMU<T>::value is true iff EstimateMemoryUsage(T) is available.
- // (This is the default version, which is false.)
- template <class T, class X = void>
- struct HasEMU : std::false_type {};
- // This HasEMU specialization is only picked up if there exists function
- // EstimateMemoryUsage(const T&) that returns size_t. Simpler ways to
- // achieve this don't work on MSVC.
- template <class T>
- struct HasEMU<
- T,
- typename std::enable_if<std::is_same<
- size_t,
- decltype(EstimateMemoryUsage(std::declval<const T&>()))>::value>::type>
- : std::true_type {};
- // EMUCaller<T> does three things:
- // 1. Defines Call() method that calls EstimateMemoryUsage(T) if it's
- // available.
- // 2. If EstimateMemoryUsage(T) is not available, but T has trivial dtor
- // (i.e. it's POD, integer, pointer, enum, etc.) then it defines Call()
- // method that returns 0. This is useful for containers, which allocate
- // memory regardless of T (also for cases like std::map<int, MyClass>).
- // 3. Finally, if EstimateMemoryUsage(T) is not available, then it triggers
- // a static_assert with a helpful message. That cuts numbers of errors
- // considerably - if you just call EstimateMemoryUsage(T) but it's not
- // available for T, then compiler will helpfully list *all* possible
- // variants of it, with an explanation for each.
- template <class T, class X = void>
- struct EMUCaller {
- // std::is_same<> below makes static_assert depend on T, in order to
- // prevent it from asserting regardless instantiation.
- static_assert(std::is_same<T, std::false_type>::value,
- "Neither global function 'size_t EstimateMemoryUsage(T)' "
- "nor member function 'size_t T::EstimateMemoryUsage() const' "
- "is defined for the type.");
- static size_t Call(const T&) { return 0; }
- };
- template <class T>
- struct EMUCaller<T, typename std::enable_if<HasEMU<T>::value>::type> {
- static size_t Call(const T& value) { return EstimateMemoryUsage(value); }
- };
- template <template <class...> class Container, class I, class = void>
- struct IsComplexIteratorForContainer : std::false_type {};
- template <template <class...> class Container, class I>
- struct IsComplexIteratorForContainer<
- Container,
- I,
- std::enable_if_t<!std::is_pointer<I>::value &&
- base::internal::is_iterator<I>::value>> {
- using value_type = typename std::iterator_traits<I>::value_type;
- using container_type = Container<value_type>;
- // We use enum instead of static constexpr bool, beause we don't have inline
- // variables until c++17.
- //
- // The downside is - value is not of type bool.
- enum : bool {
- value =
- std::is_same<typename container_type::iterator, I>::value ||
- std::is_same<typename container_type::const_iterator, I>::value ||
- std::is_same<typename container_type::reverse_iterator, I>::value ||
- std::is_same<typename container_type::const_reverse_iterator, I>::value,
- };
- };
- template <class I, template <class...> class... Containers>
- constexpr bool OneOfContainersComplexIterators() {
- // We are forced to create a temporary variable to workaround a compilation
- // error in msvs.
- const bool all_tests[] = {
- IsComplexIteratorForContainer<Containers, I>::value...};
- for (bool test : all_tests)
- if (test)
- return true;
- return false;
- }
- // std::array has an extra required template argument. We curry it.
- template <class T>
- using array_test_helper = std::array<T, 1>;
- template <class I>
- constexpr bool IsStandardContainerComplexIterator() {
- // TODO(dyaroshev): deal with maps iterators if there is a need.
- // It requires to parse pairs into keys and values.
- // TODO(dyaroshev): deal with unordered containers: they do not have reverse
- // iterators.
- return OneOfContainersComplexIterators<
- I, array_test_helper, std::vector, std::deque,
- /*std::forward_list,*/ std::list, std::set, std::multiset>();
- }
- // Work around MSVS bug. For some reason constexpr function doesn't work.
- // However variable template does.
- template <typename T>
- constexpr bool IsKnownNonAllocatingType_v =
- std::is_trivially_destructible<T>::value ||
- IsStandardContainerComplexIterator<T>();
- template <class T>
- struct EMUCaller<
- T,
- std::enable_if_t<!HasEMU<T>::value && IsKnownNonAllocatingType_v<T>>> {
- static size_t Call(const T& value) { return 0; }
- };
- } // namespace internal
- // Proxy that deducts T and calls EMUCaller<T>.
- // To be used by EstimateMemoryUsage() implementations for containers.
- template <class T>
- size_t EstimateItemMemoryUsage(const T& value) {
- return internal::EMUCaller<T>::Call(value);
- }
- template <class I>
- size_t EstimateIterableMemoryUsage(const I& iterable) {
- size_t memory_usage = 0;
- for (const auto& item : iterable) {
- memory_usage += EstimateItemMemoryUsage(item);
- }
- return memory_usage;
- }
- // Global EstimateMemoryUsage(T) that just calls T::EstimateMemoryUsage().
- template <class T>
- auto EstimateMemoryUsage(const T& object)
- -> decltype(object.EstimateMemoryUsage()) {
- static_assert(
- std::is_same<decltype(object.EstimateMemoryUsage()), size_t>::value,
- "'T::EstimateMemoryUsage() const' must return size_t.");
- return object.EstimateMemoryUsage();
- }
- // String
- template <class C, class T, class A>
- size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string) {
- using string_type = std::basic_string<C, T, A>;
- using value_type = typename string_type::value_type;
- // C++11 doesn't leave much room for implementors - std::string can
- // use short string optimization, but that's about it. We detect SSO
- // by checking that c_str() points inside |string|.
- const uint8_t* cstr = reinterpret_cast<const uint8_t*>(string.c_str());
- const uint8_t* inline_cstr = reinterpret_cast<const uint8_t*>(&string);
- if (cstr >= inline_cstr && cstr < inline_cstr + sizeof(string)) {
- // SSO string
- return 0;
- }
- return (string.capacity() + 1) * sizeof(value_type);
- }
- // Use explicit instantiations from the .cc file (reduces bloat).
- extern template BASE_EXPORT size_t EstimateMemoryUsage(const std::string&);
- extern template BASE_EXPORT size_t EstimateMemoryUsage(const std::u16string&);
- // Arrays
- template <class T, size_t N>
- size_t EstimateMemoryUsage(const std::array<T, N>& array) {
- return EstimateIterableMemoryUsage(array);
- }
- template <class T, size_t N>
- size_t EstimateMemoryUsage(T (&array)[N]) {
- return EstimateIterableMemoryUsage(array);
- }
- template <class T>
- size_t EstimateMemoryUsage(const T* array, size_t array_length) {
- size_t memory_usage = sizeof(T) * array_length;
- for (size_t i = 0; i != array_length; ++i) {
- memory_usage += EstimateItemMemoryUsage(array[i]);
- }
- return memory_usage;
- }
- // std::unique_ptr
- template <class T, class D>
- size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr) {
- return ptr ? (sizeof(T) + EstimateItemMemoryUsage(*ptr)) : 0;
- }
- template <class T, class D>
- size_t EstimateMemoryUsage(const std::unique_ptr<T[], D>& array,
- size_t array_length) {
- return EstimateMemoryUsage(array.get(), array_length);
- }
- // std::shared_ptr
- template <class T>
- size_t EstimateMemoryUsage(const std::shared_ptr<T>& ptr) {
- auto use_count = ptr.use_count();
- if (use_count == 0) {
- return 0;
- }
- // Model shared_ptr after libc++,
- // see __shared_ptr_pointer from include/memory
- struct SharedPointer {
- raw_ptr<void> vtbl;
- long shared_owners;
- long shared_weak_owners;
- raw_ptr<T> value;
- };
- // If object of size S shared N > S times we prefer to (potentially)
- // overestimate than to return 0.
- return sizeof(SharedPointer) +
- (EstimateItemMemoryUsage(*ptr) + (use_count - 1)) / use_count;
- }
- // std::pair
- template <class F, class S>
- size_t EstimateMemoryUsage(const std::pair<F, S>& pair) {
- return EstimateItemMemoryUsage(pair.first) +
- EstimateItemMemoryUsage(pair.second);
- }
- // std::vector
- template <class T, class A>
- size_t EstimateMemoryUsage(const std::vector<T, A>& vector) {
- return sizeof(T) * vector.capacity() + EstimateIterableMemoryUsage(vector);
- }
- // std::list
- template <class T, class A>
- size_t EstimateMemoryUsage(const std::list<T, A>& list) {
- using value_type = typename std::list<T, A>::value_type;
- struct Node {
- raw_ptr<Node> prev;
- raw_ptr<Node> next;
- value_type value;
- };
- return sizeof(Node) * list.size() +
- EstimateIterableMemoryUsage(list);
- }
- template <class T>
- size_t EstimateMemoryUsage(const base::LinkedList<T>& list) {
- size_t memory_usage = 0u;
- for (base::LinkNode<T>* node = list.head(); node != list.end();
- node = node->next()) {
- // Since we increment by calling node = node->next() we know that node
- // isn't nullptr.
- memory_usage += EstimateMemoryUsage(*node->value()) + sizeof(T);
- }
- return memory_usage;
- }
- // Tree containers
- template <class V>
- size_t EstimateTreeMemoryUsage(size_t size) {
- // Tree containers are modeled after libc++
- // (__tree_node from include/__tree)
- struct Node {
- raw_ptr<Node> left;
- raw_ptr<Node> right;
- raw_ptr<Node> parent;
- bool is_black;
- V value;
- };
- return sizeof(Node) * size;
- }
- template <class T, class C, class A>
- size_t EstimateMemoryUsage(const std::set<T, C, A>& set) {
- using value_type = typename std::set<T, C, A>::value_type;
- return EstimateTreeMemoryUsage<value_type>(set.size()) +
- EstimateIterableMemoryUsage(set);
- }
- template <class T, class C, class A>
- size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set) {
- using value_type = typename std::multiset<T, C, A>::value_type;
- return EstimateTreeMemoryUsage<value_type>(set.size()) +
- EstimateIterableMemoryUsage(set);
- }
- template <class K, class V, class C, class A>
- size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map) {
- using value_type = typename std::map<K, V, C, A>::value_type;
- return EstimateTreeMemoryUsage<value_type>(map.size()) +
- EstimateIterableMemoryUsage(map);
- }
- template <class K, class V, class C, class A>
- size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map) {
- using value_type = typename std::multimap<K, V, C, A>::value_type;
- return EstimateTreeMemoryUsage<value_type>(map.size()) +
- EstimateIterableMemoryUsage(map);
- }
- // HashMap containers
- namespace internal {
- // While hashtable containers model doesn't depend on STL implementation, one
- // detail still crept in: bucket_count. It's used in size estimation, but its
- // value after inserting N items is not predictable.
- // This function is specialized by unittests to return constant value, thus
- // excluding bucket_count from testing.
- template <class V>
- size_t HashMapBucketCountForTesting(size_t bucket_count) {
- return bucket_count;
- }
- template <class LruCacheType>
- size_t DoEstimateMemoryUsageForLruCache(const LruCacheType& lru_cache) {
- return EstimateMemoryUsage(lru_cache.ordering_) +
- EstimateMemoryUsage(lru_cache.index_);
- }
- } // namespace internal
- template <class V>
- size_t EstimateHashMapMemoryUsage(size_t bucket_count, size_t size) {
- // Hashtable containers are modeled after libc++
- // (__hash_node from include/__hash_table)
- struct Node {
- raw_ptr<void> next;
- size_t hash;
- V value;
- };
- using Bucket = void*;
- bucket_count = internal::HashMapBucketCountForTesting<V>(bucket_count);
- return sizeof(Bucket) * bucket_count + sizeof(Node) * size;
- }
- template <class K, class H, class KE, class A>
- size_t EstimateMemoryUsage(const std::unordered_set<K, H, KE, A>& set) {
- using value_type = typename std::unordered_set<K, H, KE, A>::value_type;
- return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
- set.size()) +
- EstimateIterableMemoryUsage(set);
- }
- template <class K, class H, class KE, class A>
- size_t EstimateMemoryUsage(const std::unordered_multiset<K, H, KE, A>& set) {
- using value_type = typename std::unordered_multiset<K, H, KE, A>::value_type;
- return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
- set.size()) +
- EstimateIterableMemoryUsage(set);
- }
- template <class K, class V, class H, class KE, class A>
- size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map) {
- using value_type = typename std::unordered_map<K, V, H, KE, A>::value_type;
- return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
- map.size()) +
- EstimateIterableMemoryUsage(map);
- }
- template <class K, class V, class H, class KE, class A>
- size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map) {
- using value_type =
- typename std::unordered_multimap<K, V, H, KE, A>::value_type;
- return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
- map.size()) +
- EstimateIterableMemoryUsage(map);
- }
- // std::deque
- template <class T, class A>
- size_t EstimateMemoryUsage(const std::deque<T, A>& deque) {
- // Since std::deque implementations are wildly different
- // (see crbug.com/674287), we can't have one "good enough"
- // way to estimate.
- // kBlockSize - minimum size of a block, in bytes
- // kMinBlockLength - number of elements in a block
- // if sizeof(T) > kBlockSize
- #if defined(_LIBCPP_VERSION)
- size_t kBlockSize = 4096;
- size_t kMinBlockLength = 16;
- #elif defined(__GLIBCXX__)
- size_t kBlockSize = 512;
- size_t kMinBlockLength = 1;
- #elif defined(_MSC_VER)
- size_t kBlockSize = 16;
- size_t kMinBlockLength = 1;
- #else
- size_t kBlockSize = 0;
- size_t kMinBlockLength = 1;
- #endif
- size_t block_length =
- (sizeof(T) > kBlockSize) ? kMinBlockLength : kBlockSize / sizeof(T);
- size_t blocks = (deque.size() + block_length - 1) / block_length;
- #if defined(__GLIBCXX__)
- // libstdc++: deque always has at least one block
- if (!blocks)
- blocks = 1;
- #endif
- #if defined(_LIBCPP_VERSION)
- // libc++: deque keeps at most two blocks when it shrinks,
- // so even if the size is zero, deque might be holding up
- // to 4096 * 2 bytes. One way to know whether deque has
- // ever allocated (and hence has 1 or 2 blocks) is to check
- // iterator's pointer. Non-zero value means that deque has
- // at least one block.
- if (!blocks && deque.begin().operator->())
- blocks = 1;
- #endif
- return (blocks * block_length * sizeof(T)) +
- EstimateIterableMemoryUsage(deque);
- }
- // Container adapters
- template <class T, class C>
- size_t EstimateMemoryUsage(const std::queue<T, C>& queue) {
- return EstimateMemoryUsage(GetUnderlyingContainer(queue));
- }
- template <class T, class C>
- size_t EstimateMemoryUsage(const std::priority_queue<T, C>& queue) {
- return EstimateMemoryUsage(GetUnderlyingContainer(queue));
- }
- template <class T, class C>
- size_t EstimateMemoryUsage(const std::stack<T, C>& stack) {
- return EstimateMemoryUsage(GetUnderlyingContainer(stack));
- }
- // base::circular_deque
- template <class T>
- size_t EstimateMemoryUsage(const base::circular_deque<T>& deque) {
- return sizeof(T) * deque.capacity() + EstimateIterableMemoryUsage(deque);
- }
- // Flat containers
- template <class T, class C>
- size_t EstimateMemoryUsage(const base::flat_set<T, C>& set) {
- using value_type = typename base::flat_set<T, C>::value_type;
- return sizeof(value_type) * set.capacity() + EstimateIterableMemoryUsage(set);
- }
- template <class K, class V, class C>
- size_t EstimateMemoryUsage(const base::flat_map<K, V, C>& map) {
- using value_type = typename base::flat_map<K, V, C>::value_type;
- return sizeof(value_type) * map.capacity() + EstimateIterableMemoryUsage(map);
- }
- template <class K, class V, class C>
- size_t EstimateMemoryUsage(const LRUCache<K, V, C>& lru_cache) {
- return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
- }
- template <class K, class V, class C>
- size_t EstimateMemoryUsage(const HashingLRUCache<K, V, C>& lru_cache) {
- return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
- }
- template <class V, class C>
- size_t EstimateMemoryUsage(const LRUCacheSet<V, C>& lru_cache) {
- return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
- }
- template <class V, class C>
- size_t EstimateMemoryUsage(const HashingLRUCacheSet<V, C>& lru_cache) {
- return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
- }
- } // namespace trace_event
- } // namespace base
- #endif // BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
|