• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
6 #define BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
7 
8 #include <stdint.h>
9 
10 #include <array>
11 #include <deque>
12 #include <list>
13 #include <map>
14 #include <memory>
15 #include <queue>
16 #include <set>
17 #include <stack>
18 #include <string>
19 #include <type_traits>
20 #include <unordered_map>
21 #include <unordered_set>
22 #include <vector>
23 
24 #include "base/base_export.h"
25 #include "base/containers/circular_deque.h"
26 #include "base/containers/flat_map.h"
27 #include "base/containers/flat_set.h"
28 #include "base/containers/linked_list.h"
29 #include "base/containers/lru_cache.h"
30 #include "base/containers/queue.h"
31 #include "base/memory/raw_ptr.h"
32 #include "base/stl_util.h"
33 #include "base/template_util.h"
34 
35 // Composable memory usage estimators.
36 //
37 // This file defines set of EstimateMemoryUsage(object) functions that return
38 // approximate dynamically allocated memory usage of their argument.
39 //
40 // The ultimate goal is to make memory usage estimation for a class simply a
41 // matter of aggregating EstimateMemoryUsage() results over all fields.
42 //
43 // That is achieved via composability: if EstimateMemoryUsage() is defined
44 // for T then EstimateMemoryUsage() is also defined for any combination of
45 // containers holding T (e.g. std::map<int, std::vector<T>>).
46 //
47 // There are two ways of defining EstimateMemoryUsage() for a type:
48 //
49 // 1. As a global function 'size_t EstimateMemoryUsage(T)' in
50 //    in base::trace_event namespace.
51 //
52 // 2. As 'size_t T::EstimateMemoryUsage() const' method. In this case
53 //    EstimateMemoryUsage(T) function in base::trace_event namespace is
54 //    provided automatically.
55 //
56 // Here is an example implementation:
57 //
58 // class MyClass {
59 //   ...
60 //   ...
61 //   size_t EstimateMemoryUsage() const {
62 //     return base::trace_event::EstimateMemoryUsage(set_) +
63 //            base::trace_event::EstimateMemoryUsage(name_) +
64 //            base::trace_event::EstimateMemoryUsage(foo_);
65 //   }
66 //   ...
67 //  private:
68 //   ...
69 //   std::set<int> set_;
70 //   std::string name_;
71 //   Foo foo_;
72 //   int id_;
73 //   bool success_;
74 // }
75 //
76 // The approach is simple: first call EstimateMemoryUsage() on all members,
77 // then recursively fix compilation errors that are caused by types not
78 // implementing EstimateMemoryUsage().
79 //
80 // Note that in the above example, the memory estimates for `id_` and `success_` are
81 // intentionally omitted. This is because these members do not allocate any _dynamic_ memory.
82 // If, for example, `MyClass` is declared as a heap-allocated `unique_ptr` member in some parent
83 // class, then `EstimateMemoryUsage` on the `unique_ptr` will automatically take into account
84 // `sizeof(MyClass)`.
85 
86 namespace base {
87 namespace trace_event {
88 
89 // Declarations
90 
91 // If T declares 'EstimateMemoryUsage() const' member function, then
92 // global function EstimateMemoryUsage(T) is available, and just calls
93 // the member function.
94 template <class T>
95 auto EstimateMemoryUsage(const T& object)
96     -> decltype(object.EstimateMemoryUsage());
97 
98 // String
99 
100 template <class C, class T, class A>
101 size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string);
102 
103 // Arrays
104 
105 template <class T, size_t N>
106 size_t EstimateMemoryUsage(const std::array<T, N>& array);
107 
108 template <class T, size_t N>
109 size_t EstimateMemoryUsage(T (&array)[N]);
110 
111 template <class T>
112 size_t EstimateMemoryUsage(const T* array, size_t array_length);
113 
114 // std::unique_ptr
115 
116 template <class T, class D>
117 size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr);
118 
119 template <class T, class D>
120 size_t EstimateMemoryUsage(const std::unique_ptr<T[], D>& array,
121                            size_t array_length);
122 
123 // std::shared_ptr
124 
125 template <class T>
126 size_t EstimateMemoryUsage(const std::shared_ptr<T>& ptr);
127 
128 // Containers
129 
130 template <class F, class S>
131 size_t EstimateMemoryUsage(const std::pair<F, S>& pair);
132 
133 template <class T, class A>
134 size_t EstimateMemoryUsage(const std::vector<T, A>& vector);
135 
136 template <class T, class A>
137 size_t EstimateMemoryUsage(const std::list<T, A>& list);
138 
139 template <class T>
140 size_t EstimateMemoryUsage(const base::LinkedList<T>& list);
141 
142 template <class T, class C, class A>
143 size_t EstimateMemoryUsage(const std::set<T, C, A>& set);
144 
145 template <class T, class C, class A>
146 size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set);
147 
148 template <class K, class V, class C, class A>
149 size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map);
150 
151 template <class K, class V, class C, class A>
152 size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map);
153 
154 template <class T, class H, class KE, class A>
155 size_t EstimateMemoryUsage(const std::unordered_set<T, H, KE, A>& set);
156 
157 template <class T, class H, class KE, class A>
158 size_t EstimateMemoryUsage(const std::unordered_multiset<T, H, KE, A>& set);
159 
160 template <class K, class V, class H, class KE, class A>
161 size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map);
162 
163 template <class K, class V, class H, class KE, class A>
164 size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map);
165 
166 template <class T, class A>
167 size_t EstimateMemoryUsage(const std::deque<T, A>& deque);
168 
169 template <class T, class C>
170 size_t EstimateMemoryUsage(const std::queue<T, C>& queue);
171 
172 template <class T, class C>
173 size_t EstimateMemoryUsage(const std::priority_queue<T, C>& queue);
174 
175 template <class T, class C>
176 size_t EstimateMemoryUsage(const std::stack<T, C>& stack);
177 
178 template <class T>
179 size_t EstimateMemoryUsage(const base::circular_deque<T>& deque);
180 
181 template <class T, class C>
182 size_t EstimateMemoryUsage(const base::flat_set<T, C>& set);
183 
184 template <class K, class V, class C>
185 size_t EstimateMemoryUsage(const base::flat_map<K, V, C>& map);
186 
187 template <class K, class V, class C>
188 size_t EstimateMemoryUsage(const base::LRUCache<K, V, C>& lru);
189 
190 template <class K, class V, class C>
191 size_t EstimateMemoryUsage(const base::HashingLRUCache<K, V, C>& lru);
192 
193 template <class V, class C>
194 size_t EstimateMemoryUsage(const base::LRUCacheSet<V, C>& lru);
195 
196 template <class V, class C>
197 size_t EstimateMemoryUsage(const base::HashingLRUCacheSet<V, C>& lru);
198 
199 // TODO(dskiba):
200 //   std::forward_list
201 
202 // Definitions
203 
204 namespace internal {
205 
206 // HasEMU<T>::value is true iff EstimateMemoryUsage(T) is available.
207 // (This is the default version, which is false.)
208 template <class T, class X = void>
209 struct HasEMU : std::false_type {};
210 
211 // This HasEMU specialization is only picked up if there exists function
212 // EstimateMemoryUsage(const T&) that returns size_t. Simpler ways to
213 // achieve this don't work on MSVC.
214 template <class T>
215 struct HasEMU<T,
216               std::enable_if_t<std::is_same_v<size_t,
217                                               decltype(EstimateMemoryUsage(
218                                                   std::declval<const T&>()))>>>
219     : std::true_type {};
220 
221 // EMUCaller<T> does three things:
222 // 1. Defines Call() method that calls EstimateMemoryUsage(T) if it's
223 //    available.
224 // 2. If EstimateMemoryUsage(T) is not available, but T has trivial dtor
225 //    (i.e. it's POD, integer, pointer, enum, etc.) then it defines Call()
226 //    method that returns 0. This is useful for containers, which allocate
227 //    memory regardless of T (also for cases like std::map<int, MyClass>).
228 // 3. Finally, if EstimateMemoryUsage(T) is not available, then it triggers
229 //    a static_assert with a helpful message. That cuts numbers of errors
230 //    considerably - if you just call EstimateMemoryUsage(T) but it's not
231 //    available for T, then compiler will helpfully list *all* possible
232 //    variants of it, with an explanation for each.
233 template <class T, class X = void>
234 struct EMUCaller {
235   // std::is_same<> below makes static_assert depend on T, in order to
236   // prevent it from asserting regardless instantiation.
237   static_assert(std::is_same_v<T, std::false_type>,
238                 "Neither global function 'size_t EstimateMemoryUsage(T)' "
239                 "nor member function 'size_t T::EstimateMemoryUsage() const' "
240                 "is defined for the type.");
241 
242   static size_t Call(const T&) { return 0; }
243 };
244 
245 template <class T>
246 struct EMUCaller<T, std::enable_if_t<HasEMU<T>::value>> {
247   static size_t Call(const T& value) { return EstimateMemoryUsage(value); }
248 };
249 
250 template <template <class...> class Container, class I, class = void>
251 struct IsComplexIteratorForContainer : std::false_type {};
252 
253 template <template <class...> class Container, class I>
254 struct IsComplexIteratorForContainer<
255     Container,
256     I,
257     std::enable_if_t<!std::is_pointer_v<I> &&
258                      base::internal::is_iterator<I>::value>> {
259   using value_type = typename std::iterator_traits<I>::value_type;
260   using container_type = Container<value_type>;
261 
262   // We use enum instead of static constexpr bool, beause we don't have inline
263   // variables until c++17.
264   //
265   // The downside is - value is not of type bool.
266   enum : bool {
267     value = std::is_same_v<typename container_type::iterator, I> ||
268             std::is_same_v<typename container_type::const_iterator, I> ||
269             std::is_same_v<typename container_type::reverse_iterator, I> ||
270             std::is_same_v<typename container_type::const_reverse_iterator, I>,
271   };
272 };
273 
274 template <class I, template <class...> class... Containers>
275 constexpr bool OneOfContainersComplexIterators() {
276   // We are forced to create a temporary variable to workaround a compilation
277   // error in msvs.
278   const bool all_tests[] = {
279       IsComplexIteratorForContainer<Containers, I>::value...};
280   for (bool test : all_tests)
281     if (test)
282       return true;
283   return false;
284 }
285 
286 // std::array has an extra required template argument. We curry it.
287 template <class T>
288 using array_test_helper = std::array<T, 1>;
289 
290 template <class I>
291 constexpr bool IsStandardContainerComplexIterator() {
292   // TODO(dyaroshev): deal with maps iterators if there is a need.
293   // It requires to parse pairs into keys and values.
294   // TODO(dyaroshev): deal with unordered containers: they do not have reverse
295   // iterators.
296   return OneOfContainersComplexIterators<
297       I, array_test_helper, std::vector, std::deque,
298       /*std::forward_list,*/ std::list, std::set, std::multiset>();
299 }
300 
301 // Work around MSVS bug. For some reason constexpr function doesn't work.
302 // However variable template does.
303 template <typename T>
304 constexpr bool IsKnownNonAllocatingType_v =
305     std::is_trivially_destructible_v<T> ||
306     IsStandardContainerComplexIterator<T>();
307 
308 template <class T>
309 struct EMUCaller<
310     T,
311     std::enable_if_t<!HasEMU<T>::value && IsKnownNonAllocatingType_v<T>>> {
312   static size_t Call(const T& value) { return 0; }
313 };
314 
315 }  // namespace internal
316 
317 // Proxy that deducts T and calls EMUCaller<T>.
318 // To be used by EstimateMemoryUsage() implementations for containers.
319 template <class T>
320 size_t EstimateItemMemoryUsage(const T& value) {
321   return internal::EMUCaller<T>::Call(value);
322 }
323 
324 template <class I>
325 size_t EstimateIterableMemoryUsage(const I& iterable) {
326   size_t memory_usage = 0;
327   for (const auto& item : iterable) {
328     memory_usage += EstimateItemMemoryUsage(item);
329   }
330   return memory_usage;
331 }
332 
333 // Global EstimateMemoryUsage(T) that just calls T::EstimateMemoryUsage().
334 template <class T>
335 auto EstimateMemoryUsage(const T& object)
336     -> decltype(object.EstimateMemoryUsage()) {
337   static_assert(std::is_same_v<decltype(object.EstimateMemoryUsage()), size_t>,
338                 "'T::EstimateMemoryUsage() const' must return size_t.");
339   return object.EstimateMemoryUsage();
340 }
341 
342 // String
343 
344 template <class C, class T, class A>
345 size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string) {
346   using string_type = std::basic_string<C, T, A>;
347   using value_type = typename string_type::value_type;
348   // C++11 doesn't leave much room for implementors - std::string can
349   // use short string optimization, but that's about it. We detect SSO
350   // by checking that c_str() points inside |string|.
351   const uint8_t* cstr = reinterpret_cast<const uint8_t*>(string.c_str());
352   const uint8_t* inline_cstr = reinterpret_cast<const uint8_t*>(&string);
353   if (cstr >= inline_cstr && cstr < inline_cstr + sizeof(string)) {
354     // SSO string
355     return 0;
356   }
357   return (string.capacity() + 1) * sizeof(value_type);
358 }
359 
360 // Use explicit instantiations from the .cc file (reduces bloat).
361 extern template BASE_EXPORT size_t EstimateMemoryUsage(const std::string&);
362 extern template BASE_EXPORT size_t EstimateMemoryUsage(const std::u16string&);
363 
364 // Arrays
365 
366 template <class T, size_t N>
367 size_t EstimateMemoryUsage(const std::array<T, N>& array) {
368   return EstimateIterableMemoryUsage(array);
369 }
370 
371 template <class T, size_t N>
372 size_t EstimateMemoryUsage(T (&array)[N]) {
373   return EstimateIterableMemoryUsage(array);
374 }
375 
376 template <class T>
377 size_t EstimateMemoryUsage(const T* array, size_t array_length) {
378   size_t memory_usage = sizeof(T) * array_length;
379   for (size_t i = 0; i != array_length; ++i) {
380     memory_usage += EstimateItemMemoryUsage(array[i]);
381   }
382   return memory_usage;
383 }
384 
385 // std::unique_ptr
386 
387 template <class T, class D>
388 size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr) {
389   return ptr ? (sizeof(T) + EstimateItemMemoryUsage(*ptr)) : 0;
390 }
391 
392 template <class T, class D>
393 size_t EstimateMemoryUsage(const std::unique_ptr<T[], D>& array,
394                            size_t array_length) {
395   return EstimateMemoryUsage(array.get(), array_length);
396 }
397 
398 // std::shared_ptr
399 
400 template <class T>
401 size_t EstimateMemoryUsage(const std::shared_ptr<T>& ptr) {
402   auto use_count = ptr.use_count();
403   if (use_count == 0) {
404     return 0;
405   }
406   // Model shared_ptr after libc++,
407   // see __shared_ptr_pointer from include/memory
408   struct SharedPointer {
409     raw_ptr<void> vtbl;
410     long shared_owners;
411     long shared_weak_owners;
412     raw_ptr<T> value;
413   };
414   // If object of size S shared N > S times we prefer to (potentially)
415   // overestimate than to return 0.
416   return sizeof(SharedPointer) +
417          (EstimateItemMemoryUsage(*ptr) + (use_count - 1)) / use_count;
418 }
419 
420 // std::pair
421 
422 template <class F, class S>
423 size_t EstimateMemoryUsage(const std::pair<F, S>& pair) {
424   return EstimateItemMemoryUsage(pair.first) +
425          EstimateItemMemoryUsage(pair.second);
426 }
427 
428 // std::vector
429 
430 template <class T, class A>
431 size_t EstimateMemoryUsage(const std::vector<T, A>& vector) {
432   return sizeof(T) * vector.capacity() + EstimateIterableMemoryUsage(vector);
433 }
434 
435 // std::list
436 
437 template <class T, class A>
438 size_t EstimateMemoryUsage(const std::list<T, A>& list) {
439   using value_type = typename std::list<T, A>::value_type;
440   struct Node {
441     raw_ptr<Node> prev;
442     raw_ptr<Node> next;
443     value_type value;
444   };
445   return sizeof(Node) * list.size() +
446          EstimateIterableMemoryUsage(list);
447 }
448 
449 template <class T>
450 size_t EstimateMemoryUsage(const base::LinkedList<T>& list) {
451   size_t memory_usage = 0u;
452   for (base::LinkNode<T>* node = list.head(); node != list.end();
453        node = node->next()) {
454     // Since we increment by calling node = node->next() we know that node
455     // isn't nullptr.
456     memory_usage += EstimateMemoryUsage(*node->value()) + sizeof(T);
457   }
458   return memory_usage;
459 }
460 
461 // Tree containers
462 
463 template <class V>
464 size_t EstimateTreeMemoryUsage(size_t size) {
465   // Tree containers are modeled after libc++
466   // (__tree_node from include/__tree)
467   struct Node {
468     raw_ptr<Node> left;
469     raw_ptr<Node> right;
470     raw_ptr<Node> parent;
471     bool is_black;
472     V value;
473   };
474   return sizeof(Node) * size;
475 }
476 
477 template <class T, class C, class A>
478 size_t EstimateMemoryUsage(const std::set<T, C, A>& set) {
479   using value_type = typename std::set<T, C, A>::value_type;
480   return EstimateTreeMemoryUsage<value_type>(set.size()) +
481          EstimateIterableMemoryUsage(set);
482 }
483 
484 template <class T, class C, class A>
485 size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set) {
486   using value_type = typename std::multiset<T, C, A>::value_type;
487   return EstimateTreeMemoryUsage<value_type>(set.size()) +
488          EstimateIterableMemoryUsage(set);
489 }
490 
491 template <class K, class V, class C, class A>
492 size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map) {
493   using value_type = typename std::map<K, V, C, A>::value_type;
494   return EstimateTreeMemoryUsage<value_type>(map.size()) +
495          EstimateIterableMemoryUsage(map);
496 }
497 
498 template <class K, class V, class C, class A>
499 size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map) {
500   using value_type = typename std::multimap<K, V, C, A>::value_type;
501   return EstimateTreeMemoryUsage<value_type>(map.size()) +
502          EstimateIterableMemoryUsage(map);
503 }
504 
505 // HashMap containers
506 
507 namespace internal {
508 
509 // While hashtable containers model doesn't depend on STL implementation, one
510 // detail still crept in: bucket_count. It's used in size estimation, but its
511 // value after inserting N items is not predictable.
512 // This function is specialized by unittests to return constant value, thus
513 // excluding bucket_count from testing.
514 template <class V>
515 size_t HashMapBucketCountForTesting(size_t bucket_count) {
516   return bucket_count;
517 }
518 
519 template <class LruCacheType>
520 size_t DoEstimateMemoryUsageForLruCache(const LruCacheType& lru_cache) {
521   return EstimateMemoryUsage(lru_cache.ordering_) +
522          EstimateMemoryUsage(lru_cache.index_);
523 }
524 
525 }  // namespace internal
526 
527 template <class V>
528 size_t EstimateHashMapMemoryUsage(size_t bucket_count, size_t size) {
529   // Hashtable containers are modeled after libc++
530   // (__hash_node from include/__hash_table)
531   struct Node {
532     raw_ptr<void> next;
533     size_t hash;
534     V value;
535   };
536   using Bucket = void*;
537   bucket_count = internal::HashMapBucketCountForTesting<V>(bucket_count);
538   return sizeof(Bucket) * bucket_count + sizeof(Node) * size;
539 }
540 
541 template <class K, class H, class KE, class A>
542 size_t EstimateMemoryUsage(const std::unordered_set<K, H, KE, A>& set) {
543   using value_type = typename std::unordered_set<K, H, KE, A>::value_type;
544   return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
545                                                 set.size()) +
546          EstimateIterableMemoryUsage(set);
547 }
548 
549 template <class K, class H, class KE, class A>
550 size_t EstimateMemoryUsage(const std::unordered_multiset<K, H, KE, A>& set) {
551   using value_type = typename std::unordered_multiset<K, H, KE, A>::value_type;
552   return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
553                                                 set.size()) +
554          EstimateIterableMemoryUsage(set);
555 }
556 
557 template <class K, class V, class H, class KE, class A>
558 size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map) {
559   using value_type = typename std::unordered_map<K, V, H, KE, A>::value_type;
560   return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
561                                                 map.size()) +
562          EstimateIterableMemoryUsage(map);
563 }
564 
565 template <class K, class V, class H, class KE, class A>
566 size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map) {
567   using value_type =
568       typename std::unordered_multimap<K, V, H, KE, A>::value_type;
569   return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
570                                                 map.size()) +
571          EstimateIterableMemoryUsage(map);
572 }
573 
574 // std::deque
575 
576 template <class T, class A>
577 size_t EstimateMemoryUsage(const std::deque<T, A>& deque) {
578 // Since std::deque implementations are wildly different
579 // (see crbug.com/674287), we can't have one "good enough"
580 // way to estimate.
581 
582 // kBlockSize      - minimum size of a block, in bytes
583 // kMinBlockLength - number of elements in a block
584 //                   if sizeof(T) > kBlockSize
585 #if defined(_LIBCPP_VERSION)
586   size_t kBlockSize = 4096;
587   size_t kMinBlockLength = 16;
588 #elif defined(__GLIBCXX__)
589   size_t kBlockSize = 512;
590   size_t kMinBlockLength = 1;
591 #elif defined(_MSC_VER)
592   size_t kBlockSize = 16;
593   size_t kMinBlockLength = 1;
594 #else
595   size_t kBlockSize = 0;
596   size_t kMinBlockLength = 1;
597 #endif
598 
599   size_t block_length =
600       (sizeof(T) > kBlockSize) ? kMinBlockLength : kBlockSize / sizeof(T);
601 
602   size_t blocks = (deque.size() + block_length - 1) / block_length;
603 
604 #if defined(__GLIBCXX__)
605   // libstdc++: deque always has at least one block
606   if (!blocks)
607     blocks = 1;
608 #endif
609 
610 #if defined(_LIBCPP_VERSION)
611   // libc++: deque keeps at most two blocks when it shrinks,
612   // so even if the size is zero, deque might be holding up
613   // to 4096 * 2 bytes. One way to know whether deque has
614   // ever allocated (and hence has 1 or 2 blocks) is to check
615   // iterator's pointer. Non-zero value means that deque has
616   // at least one block.
617   if (!blocks && deque.begin().operator->())
618     blocks = 1;
619 #endif
620 
621   return (blocks * block_length * sizeof(T)) +
622          EstimateIterableMemoryUsage(deque);
623 }
624 
625 // Container adapters
626 
627 template <class T, class C>
628 size_t EstimateMemoryUsage(const std::queue<T, C>& queue) {
629   return EstimateMemoryUsage(GetUnderlyingContainer(queue));
630 }
631 
632 template <class T, class C>
633 size_t EstimateMemoryUsage(const std::priority_queue<T, C>& queue) {
634   return EstimateMemoryUsage(GetUnderlyingContainer(queue));
635 }
636 
637 template <class T, class C>
638 size_t EstimateMemoryUsage(const std::stack<T, C>& stack) {
639   return EstimateMemoryUsage(GetUnderlyingContainer(stack));
640 }
641 
642 // base::circular_deque
643 
644 template <class T>
645 size_t EstimateMemoryUsage(const base::circular_deque<T>& deque) {
646   return sizeof(T) * deque.capacity() + EstimateIterableMemoryUsage(deque);
647 }
648 
649 // Flat containers
650 
651 template <class T, class C>
652 size_t EstimateMemoryUsage(const base::flat_set<T, C>& set) {
653   using value_type = typename base::flat_set<T, C>::value_type;
654   return sizeof(value_type) * set.capacity() + EstimateIterableMemoryUsage(set);
655 }
656 
657 template <class K, class V, class C>
658 size_t EstimateMemoryUsage(const base::flat_map<K, V, C>& map) {
659   using value_type = typename base::flat_map<K, V, C>::value_type;
660   return sizeof(value_type) * map.capacity() + EstimateIterableMemoryUsage(map);
661 }
662 
663 template <class K, class V, class C>
664 size_t EstimateMemoryUsage(const LRUCache<K, V, C>& lru_cache) {
665   return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
666 }
667 
668 template <class K, class V, class C>
669 size_t EstimateMemoryUsage(const HashingLRUCache<K, V, C>& lru_cache) {
670   return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
671 }
672 
673 template <class V, class C>
674 size_t EstimateMemoryUsage(const LRUCacheSet<V, C>& lru_cache) {
675   return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
676 }
677 
678 template <class V, class C>
679 size_t EstimateMemoryUsage(const HashingLRUCacheSet<V, C>& lru_cache) {
680   return internal::DoEstimateMemoryUsageForLruCache(lru_cache);
681 }
682 
683 }  // namespace trace_event
684 }  // namespace base
685 
686 #endif  // BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
687