Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(258)

Side by Side Diff: base/trace_event/estimate_memory_usage.h

Issue 2440393002: [tracing] Implement composable memory usage estimators. (Closed)
Patch Set: Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/test/scoped_memory_usage.cc ('k') | base/trace_event/estimate_memory_usage_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
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_ESTIMATE_MEMORY_USAGE_H_
6 #define BASE_TRACE_EVENT_ESTIMATE_MEMORY_USAGE_H_
7
8 #include <array>
9 #include <list>
10 #include <map>
11 #include <memory>
12 #include <set>
13 #include <string>
14 #include <type_traits>
15 #include <unordered_map>
16 #include <unordered_set>
17 #include <vector>
18
19 #include "base/template_util.h"
20
21 // Composable memory usage estimators.
22 //
23 // This file defines set of EstimateMemoryUsage(object) functions that return
24 // approximate memory usage of their argument.
25 //
26 // The ultimate goal is to make memory usage estimation for a class simply a
27 // matter of aggregating EstimateMemoryUsage() results over all fields.
28 //
29 // That is achieved via composability: if EstimateMemoryUsage() is defined
30 // for T then EstimateMemoryUsage() is also defined for any combination of
31 // containers holding T (e.g. std::map<int, std::vector<T>>).
32 //
33 // There are two ways of defining EstimateMemoryUsage() for a type:
34 //
35 // 1. As a global function 'size_t EstimateMemoryUsage(T)' in type's namespace
36 // (or as a last resort, in base::trace_event namespace).
37 //
38 // 2. As 'size_t T::EstimateMemoryUsage() const' method. In this case global
39 // EstimateMemoryUsage(T) function in base::trace_event namespace is
40 // provided automatically.
41 //
42 // Here is an example implementation:
43 //
44 // size_t foo::bar::MyClass::EstimateMemoryUsage() const {
45 // using base::trace_event::EstimateMemoryUsage;
46 // return EstimateMemoryUsage(name_) +
47 // EstimateMemoryUsage(id_) +
48 // EstimateMemoryUsage(items_);
49 // }
50 //
51 // Two things to note:
52 //
53 // 1. It starts with 'using' declaration. This makes everything defined in
54 // this file (i.e. all EstimateMemoryUsage variants) available for compiler
55 // to choose from.
56 //
57 // 2. It just calls EstimateMemoryUsage() on all (suitable) members.
58 // The pattern is simple: first call EstimateMemoryUsage() on all members,
59 // then fix compilation errors that are caused by types not implementing
60 // EstimateMemoryUsage().
61
62 namespace base {
63 namespace trace_event {
64
65 // Declarations
66
67 // If T declares 'EstimateMemoryUsage() const' member function, then
68 // global function EstimateMemoryUsage(T) is available, and just calls
69 // the member function.
70 template <class T>
71 auto EstimateMemoryUsage(const T& object) ->
72 decltype(object.EstimateMemoryUsage());
73
74 // String
75
76 template <class C, class T, class A>
77 size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string);
78
79 // Arrays
80
81 template <class T, size_t N>
82 size_t EstimateMemoryUsage(const std::array<T, N>& array);
83
84 template <class T, size_t N>
85 size_t EstimateMemoryUsage(T (&array)[N]);
86
87 template <class T>
88 size_t EstimateMemoryUsage(const T* array, size_t array_length);
89
90 // std::unique_ptr
91
92 template <class T>
93 size_t EstimateMemoryUsage(const std::unique_ptr<T>& ptr);
94
95 template <class T>
96 size_t EstimateMemoryUsage(const std::unique_ptr<T[]>& array,
97 size_t array_length);
98
99 // Containers
100
101 template <class F, class S>
102 size_t EstimateMemoryUsage(const std::pair<F, S>& pair);
103
104 template <class T, class A>
105 size_t EstimateMemoryUsage(const std::vector<T, A>& vector);
106
107 template <class T, class A>
108 size_t EstimateMemoryUsage(const std::list<T, A>& list);
109
110 template <class T, class C, class A>
111 size_t EstimateMemoryUsage(const std::set<T, C, A>& set);
112
113 template <class T, class C, class A>
114 size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set);
115
116 template <class K, class V, class C, class A>
117 size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map);
118
119 template <class K, class V, class C, class A>
120 size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map);
121
122 template <class T, class H, class KE, class A>
123 size_t EstimateMemoryUsage(const std::unordered_set<T, H, KE, A>& set);
124
125 template <class T, class H, class KE, class A>
126 size_t EstimateMemoryUsage(const std::unordered_multiset<T, H, KE, A>& set);
127
128 template <class K, class V, class H, class KE, class A>
129 size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map);
130
131 template <class K, class V, class H, class KE, class A>
132 size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map);
133
134 // TODO(dskiba):
135 // std::forward_list
136 // std::deque
137 // std::queue
138 // std::stack
139 // std::queue
140 // std::priority_queue
141
142 // Definitions
143
144 namespace internal {
145
146 // HasEMU<T>(0) returns true if EstimateMemoryUsage(T) is available.
147 // The function is constexpr, and can be used in templates.
148 template <class T>
149 constexpr bool HasEMU(long) {
150 return false;
151 }
152
153 template <class T>
154 constexpr auto HasEMU(int)
155 -> decltype(EstimateMemoryUsage(std::declval<const T&>()), bool()) {
156 static_assert(
157 std::is_same<decltype(EstimateMemoryUsage(std::declval<const T&>())),
158 size_t>::value,
159 "EstimateMemoryUsage(T) must return size_t.");
160 return true;
161 }
162
163 // EMUCaller<T> does three things:
164 // 1. Defines Call() method that calls EstimateMemoryUsage(T) if it's
165 // available.
166 // 2. If EstimateMemoryUsage(T) is not available, but T has trivial dtor
167 // (i.e. it's POD, integer, pointer, enum, etc.) then it defines Call()
168 // method that returns 0. This is useful for containers, which allocate
169 // memory regardless of T (also for cases like std::map<int, MyClass>).
170 // 3. Finally, if EstimateMemoryUsage(T) is not available, then it triggers
171 // a static_assert with a helpful message. That cuts numbers of errors
172 // considerably - if you just call EstimateMemoryUsage(T) but it's not
173 // available for T, then compiler will helpfully list *all* possible
174 // variants of it, with an explanation for each.
175 template <class T, class X = void>
176 struct EMUCaller {
177 static_assert(
178 std::is_same<T, X>::value,
179 "Neither global function 'size_t EstimateMemoryUsage(T)' "
180 "nor member function 'size_t T::EstimateMemoryUsage() const' "
181 "is defined for the type.");
182
183 static size_t Call(const T&) {
184 return 0;
185 }
186 };
187
188 template <class T>
189 struct EMUCaller<T, typename std::enable_if<HasEMU<T>(0)>::type> {
190 static size_t Call(const T& value) {
191 return EstimateMemoryUsage(value);
192 }
193 };
194
195 template <class T>
196 struct EMUCaller<
197 T,
198 typename std::enable_if<
199 !HasEMU<T>(0) && is_trivially_destructible<T>::value
200 >::type> {
201 static size_t Call(const T& value) {
202 return 0;
203 }
204 };
205
206 } // namespace internal
207
208 // Proxy that deducts T and calls EMUCaller<T>.
209 // To be used by EstimateMemoryUsage() implementations for containers.
210 template <class T>
211 size_t EstimateItemMemoryUsage(const T& value) {
212 return internal::EMUCaller<T>::Call(value);
213 }
214
215 template <class I>
216 size_t EstimateIterableMemoryUsage(const I& iterable) {
217 size_t memory_usage = 0;
218 for (const auto& item: iterable) {
219 memory_usage += EstimateItemMemoryUsage(item);
220 }
221 return memory_usage;
222 }
223
224 // Global EstimateMemoryUsage(T) that just calls T::EstimateMemoryUsage().
225 template <class T>
226 auto EstimateMemoryUsage(const T& object) ->
227 decltype(object.EstimateMemoryUsage()) {
228 static_assert(std::is_same<decltype(object.EstimateMemoryUsage()),
229 size_t>::value,
230 "'T::EstimateMemoryUsage() const' must return size_t.");
231 return object.EstimateMemoryUsage();
232 }
233
234 // String
235
236 template <class C, class T, class A>
237 size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string) {
238 using string_type = std::basic_string<C, T, A>;
239 using value_type = typename string_type::value_type;
240 #if defined(__GLIBCXX__) && _GLIBCXX_USE_CXX11_ABI == 0
241 // libstdc++ with COW std::string - each string allocates a header
242 // (see std::basic_string::_Rep). We don't take into account number
243 // of references, but we do handle 'empty string' case.
244 struct Header {
245 typename string_type::size_type length;
246 typename string_type::size_type capacity;
247 int refcount;
248 };
249 // There is one shared empty string, which we estimate to 0.
250 static const char* empty_cstr = nullptr;
251 if (!empty_cstr) empty_cstr = std::string().c_str();
252 return (string.c_str() == empty_cstr) ?
253 0 :
254 sizeof(Header) + (string.capacity() + 1) * sizeof(value_type);
255 #else
256 // C++11 doesn't leave much room for implementors - std::string can
257 // use short string optimization, but that's about it. We detect SSO
258 // by checking that c_str() points inside |string|.
259 const char* cstr = reinterpret_cast<const char*>(string.c_str());
260 const char* inline_cstr = reinterpret_cast<const char*>(&string);
261 if (cstr >= inline_cstr && cstr < inline_cstr + sizeof(string)) {
262 // SSO string
263 return 0;
264 }
265 return (string.capacity() + 1) * sizeof(value_type);
266 #endif
267 }
268
269 // Arrays
270
271 template <class T, size_t N>
272 size_t EstimateMemoryUsage(const std::array<T, N>& array) {
273 return EstimateIterableMemoryUsage(array);
274 }
275
276 template <class T, size_t N>
277 size_t EstimateMemoryUsage(T (&array)[N]) {
278 return EstimateIterableMemoryUsage(array);
279 }
280
281 template <class T>
282 size_t EstimateMemoryUsage(const T* array, size_t array_length) {
283 size_t memory_usage = sizeof(T) * array_length;
284 for (size_t i = 0; i != array_length; ++i) {
285 memory_usage += EstimateItemMemoryUsage(array[i]);
286 }
287 return memory_usage;
288 }
289
290 // std::unique_ptr
291
292 template <class T>
293 size_t EstimateMemoryUsage(const std::unique_ptr<T>& ptr) {
294 return ptr ? (sizeof(T) + EstimateItemMemoryUsage(*ptr)) : 0;
295 }
296
297 template <class T>
298 size_t EstimateMemoryUsage(const std::unique_ptr<T[]>& array,
299 size_t array_length) {
300 return EstimateMemoryUsage(array.get(), array_length);
301 }
302
303 // std::pair
304
305 template <class F, class S>
306 size_t EstimateMemoryUsage(const std::pair<F, S>& pair) {
307 return EstimateItemMemoryUsage(pair.first) +
308 EstimateItemMemoryUsage(pair.second);
309 }
310
311 // std::vector
312
313 template <class T, class A>
314 size_t EstimateMemoryUsage(const std::vector<T, A>& vector) {
315 return sizeof(T) * vector.capacity() +
316 EstimateIterableMemoryUsage(vector);
317 }
318
319 // std::list
320
321 template <class T, class A>
322 size_t EstimateMemoryUsage(const std::list<T, A>& list) {
323 using value_type = typename std::list<T, A>::value_type;
324 struct Node {
325 Node* prev;
326 Node* next;
327 value_type value;
328 };
329 return sizeof(Node) * list.size() +
330 EstimateIterableMemoryUsage(list);
331 }
332
333 // Tree containers
334
335 template <class V>
336 size_t EstimateTreeMemoryUsage(size_t size) {
337 #if defined(__GLIBCXX__)
338 // libstdc++: _Rb_tree_node<> from include/bits/stl_tree.h
339 struct Node {
340 bool color;
341 Node* parent;
342 Node* left;
343 Node* right;
344 V value;
345 };
346 #else
347 // libc++: __tree_node<> from include/__tree
348 struct Node {
349 Node* left;
350 Node* right;
351 Node* parent;
352 bool is_black;
353 V value;
354 };
355 #endif
356 return sizeof(Node) * size;
357 }
358
359 template <class T, class C, class A>
360 size_t EstimateMemoryUsage(const std::set<T, C, A>& set) {
361 using value_type = typename std::set<T, C, A>::value_type;
362 return EstimateTreeMemoryUsage<value_type>(set.size()) +
363 EstimateIterableMemoryUsage(set);
364 }
365
366 template <class T, class C, class A>
367 size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set) {
368 using value_type = typename std::multiset<T, C, A>::value_type;
369 return EstimateTreeMemoryUsage<value_type>(set.size()) +
370 EstimateIterableMemoryUsage(set);
371 }
372
373 template <class K, class V, class C, class A>
374 size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map) {
375 using value_type = typename std::map<K, V, C, A>::value_type;
376 return EstimateTreeMemoryUsage<value_type>(map.size()) +
377 EstimateIterableMemoryUsage(map);
378 }
379
380 template <class K, class V, class C, class A>
381 size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map) {
382 using value_type = typename std::multimap<K, V, C, A>::value_type;
383 return EstimateTreeMemoryUsage<value_type>(map.size()) +
384 EstimateIterableMemoryUsage(map);
385 }
386
387 // HashMap containers
388
389 template <class V>
390 size_t EstimateHashMapMemoryUsage(size_t bucket_count, size_t size) {
391 #if defined(__GLIBCXX__)
392 // libstdc++: _Hash_node<T, false> from include/bits/hashtable_policy.h
393 struct Node {
394 V value;
395 Node* next;
396 };
397 using Bucket = Node*;
398 // _Hashtable::_M_allocate_buckets() from include/bits/hashtable.h
399 // allocates one extra bucket.
400 return sizeof(Bucket) * (bucket_count + 1) + sizeof(Node) * size;
401 #else
402 // libc++: __hash_node<> from include/__hash_table
403 struct Node {
404 void* next;
405 size_t hash;
406 V value;
407 };
408 using Bucket = void*;
409 return sizeof(Bucket) * bucket_count + sizeof(Node) * size;
410 #endif
411 }
412
413 template <class K, class H, class KE, class A>
414 size_t EstimateMemoryUsage(
415 const std::unordered_set<K, H, KE, A>& set) {
416 using value_type = typename std::unordered_set<K, H, KE, A>::value_type;
417 return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
418 set.size()) +
419 EstimateIterableMemoryUsage(set);
420 }
421
422 template <class K, class H, class KE, class A>
423 size_t EstimateMemoryUsage(
424 const std::unordered_multiset<K, H, KE, A>& set) {
425 using value_type = typename std::unordered_multiset<K, H, KE, A>::value_type;
426 return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
427 set.size()) +
428 EstimateIterableMemoryUsage(set);
429 }
430
431 template <class K, class V, class H, class KE, class A>
432 size_t EstimateMemoryUsage(
433 const std::unordered_map<K, V, H, KE, A>& map) {
434 using value_type = typename std::unordered_map<K, V, H, KE, A>::value_type;
435 return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
436 map.size()) +
437 EstimateIterableMemoryUsage(map);
438 }
439
440 template <class K, class V, class H, class KE, class A>
441 size_t EstimateMemoryUsage(
442 const std::unordered_multimap<K, V, H, KE, A>& map) {
443 using value_type =
444 typename std::unordered_multimap<K, V, H, KE, A>::value_type;
445 return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
446 map.size()) +
447 EstimateIterableMemoryUsage(map);
448 }
449
450 } // namespace trace_event
451 } // namespace base
452
453 #endif // BASE_TRACE_EVENT_ESTIMATE_MEMORY_USAGE_H_
OLDNEW
« no previous file with comments | « base/test/scoped_memory_usage.cc ('k') | base/trace_event/estimate_memory_usage_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698