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

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

Issue 2440393002: [tracing] Implement composable memory usage estimators. (Closed)
Patch Set: Use ARCH_CPU_64_BITS instead of __LP64__ 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/BUILD.gn ('k') | base/trace_event/memory_usage_estimator_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_MEMORY_USAGE_ESTIMATOR_H_
6 #define BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_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
36 // in base::trace_event namespace.
37 //
38 // 2. As 'size_t T::EstimateMemoryUsage() const' method. In this case
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 // return base::trace_event::EstimateMemoryUsage(name_) +
46 // base::trace_event::EstimateMemoryUsage(id_) +
47 // base::trace_event::EstimateMemoryUsage(items_);
48 // }
49 //
50 // The approach is simple: first call EstimateMemoryUsage() on all members,
51 // then recursively fix compilation errors that are caused by types not
52 // implementing EstimateMemoryUsage().
53
54 namespace base {
55 namespace trace_event {
56
57 // Declarations
58
59 // If T declares 'EstimateMemoryUsage() const' member function, then
60 // global function EstimateMemoryUsage(T) is available, and just calls
61 // the member function.
62 template <class T>
63 auto EstimateMemoryUsage(const T& object)
64 -> decltype(object.EstimateMemoryUsage());
65
66 // String
67
68 template <class C, class T, class A>
69 size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string);
70
71 // Arrays
72
73 template <class T, size_t N>
74 size_t EstimateMemoryUsage(const std::array<T, N>& array);
75
76 template <class T, size_t N>
77 size_t EstimateMemoryUsage(T (&array)[N]);
78
79 template <class T>
80 size_t EstimateMemoryUsage(const T* array, size_t array_length);
81
82 // std::unique_ptr
83
84 template <class T, class D>
85 size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr);
86
87 template <class T, class D>
88 size_t EstimateMemoryUsage(const std::unique_ptr<T[], D>& array,
89 size_t array_length);
90
91 // Containers
92
93 template <class F, class S>
94 size_t EstimateMemoryUsage(const std::pair<F, S>& pair);
95
96 template <class T, class A>
97 size_t EstimateMemoryUsage(const std::vector<T, A>& vector);
98
99 template <class T, class A>
100 size_t EstimateMemoryUsage(const std::list<T, A>& list);
101
102 template <class T, class C, class A>
103 size_t EstimateMemoryUsage(const std::set<T, C, A>& set);
104
105 template <class T, class C, class A>
106 size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set);
107
108 template <class K, class V, class C, class A>
109 size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map);
110
111 template <class K, class V, class C, class A>
112 size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map);
113
114 template <class T, class H, class KE, class A>
115 size_t EstimateMemoryUsage(const std::unordered_set<T, H, KE, A>& set);
116
117 template <class T, class H, class KE, class A>
118 size_t EstimateMemoryUsage(const std::unordered_multiset<T, H, KE, A>& set);
119
120 template <class K, class V, class H, class KE, class A>
121 size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map);
122
123 template <class K, class V, class H, class KE, class A>
124 size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map);
125
126 // TODO(dskiba):
127 // std::forward_list
128 // std::deque
129 // std::queue
130 // std::stack
131 // std::queue
132 // std::priority_queue
133
134 // Definitions
135
136 namespace internal {
137
138 // HasEMU<T>::value is true iff EstimateMemoryUsage(T) is available.
139 // (This is the default version, which is false.)
140 template <class T, class X = void>
141 struct HasEMU : std::false_type {};
142
143 // This HasEMU specialization is only picked up if there exists function
144 // EstimateMemoryUsage(const T&) that returns size_t. Simpler ways to
145 // achieve this don't work on MSVC.
146 template <class T>
147 struct HasEMU<
148 T,
149 typename std::enable_if<std::is_same<
150 size_t,
151 decltype(EstimateMemoryUsage(std::declval<const T&>()))>::value>::type>
152 : std::true_type {};
153
154 // EMUCaller<T> does three things:
155 // 1. Defines Call() method that calls EstimateMemoryUsage(T) if it's
156 // available.
157 // 2. If EstimateMemoryUsage(T) is not available, but T has trivial dtor
158 // (i.e. it's POD, integer, pointer, enum, etc.) then it defines Call()
159 // method that returns 0. This is useful for containers, which allocate
160 // memory regardless of T (also for cases like std::map<int, MyClass>).
161 // 3. Finally, if EstimateMemoryUsage(T) is not available, then it triggers
162 // a static_assert with a helpful message. That cuts numbers of errors
163 // considerably - if you just call EstimateMemoryUsage(T) but it's not
164 // available for T, then compiler will helpfully list *all* possible
165 // variants of it, with an explanation for each.
166 template <class T, class X = void>
dcheng 2016/11/10 06:45:46 Nit: I think class X = void may no longer be neces
167 struct EMUCaller {
168 // std::is_same<> below makes static_assert depend on T, in order to
169 // prevent it from asserting regardless instantiation.
170 static_assert(std::is_same<T, std::false_type>::value,
dcheng 2016/11/10 06:45:46 It's technically possible for std::false to be sup
171 "Neither global function 'size_t EstimateMemoryUsage(T)' "
172 "nor member function 'size_t T::EstimateMemoryUsage() const' "
173 "is defined for the type.");
174
175 static size_t Call(const T&) { return 0; }
176 };
177
178 template <class T>
179 struct EMUCaller<T, typename std::enable_if<HasEMU<T>::value>::type> {
180 static size_t Call(const T& value) { return EstimateMemoryUsage(value); }
181 };
182
183 template <class T>
184 struct EMUCaller<
185 T,
186 typename std::enable_if<!HasEMU<T>::value &&
187 is_trivially_destructible<T>::value>::type> {
188 static size_t Call(const T& value) { return 0; }
189 };
190
191 } // namespace internal
192
193 // Proxy that deducts T and calls EMUCaller<T>.
194 // To be used by EstimateMemoryUsage() implementations for containers.
195 template <class T>
196 size_t EstimateItemMemoryUsage(const T& value) {
197 return internal::EMUCaller<T>::Call(value);
198 }
199
200 template <class I>
201 size_t EstimateIterableMemoryUsage(const I& iterable) {
202 size_t memory_usage = 0;
203 for (const auto& item : iterable) {
204 memory_usage += EstimateItemMemoryUsage(item);
205 }
206 return memory_usage;
207 }
208
209 // Global EstimateMemoryUsage(T) that just calls T::EstimateMemoryUsage().
210 template <class T>
211 auto EstimateMemoryUsage(const T& object)
212 -> decltype(object.EstimateMemoryUsage()) {
213 static_assert(
214 std::is_same<decltype(object.EstimateMemoryUsage()), size_t>::value,
215 "'T::EstimateMemoryUsage() const' must return size_t.");
216 return object.EstimateMemoryUsage();
217 }
218
219 // String
220
221 template <class C, class T, class A>
222 size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string) {
223 using string_type = std::basic_string<C, T, A>;
224 using value_type = typename string_type::value_type;
225 // C++11 doesn't leave much room for implementors - std::string can
226 // use short string optimization, but that's about it. We detect SSO
227 // by checking that c_str() points inside |string|.
228 const char* cstr = reinterpret_cast<const char*>(string.c_str());
dcheng 2016/11/10 06:45:46 Perhaps use uint8_t to make it clear that we expli
229 const char* inline_cstr = reinterpret_cast<const char*>(&string);
230 if (cstr >= inline_cstr && cstr < inline_cstr + sizeof(string)) {
231 // SSO string
232 return 0;
233 }
234 return (string.capacity() + 1) * sizeof(value_type);
235 }
236
237 // Arrays
238
239 template <class T, size_t N>
240 size_t EstimateMemoryUsage(const std::array<T, N>& array) {
241 return EstimateIterableMemoryUsage(array);
242 }
243
244 template <class T, size_t N>
245 size_t EstimateMemoryUsage(T (&array)[N]) {
246 return EstimateIterableMemoryUsage(array);
247 }
248
249 template <class T>
250 size_t EstimateMemoryUsage(const T* array, size_t array_length) {
251 size_t memory_usage = sizeof(T) * array_length;
252 for (size_t i = 0; i != array_length; ++i) {
253 memory_usage += EstimateItemMemoryUsage(array[i]);
254 }
255 return memory_usage;
256 }
257
258 // std::unique_ptr
259
260 template <class T, class D>
261 size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr) {
262 return ptr ? (sizeof(T) + EstimateItemMemoryUsage(*ptr)) : 0;
263 }
264
265 template <class T, class D>
266 size_t EstimateMemoryUsage(const std::unique_ptr<T[], D>& array,
267 size_t array_length) {
268 return EstimateMemoryUsage(array.get(), array_length);
269 }
270
271 // std::pair
272
273 template <class F, class S>
274 size_t EstimateMemoryUsage(const std::pair<F, S>& pair) {
275 return EstimateItemMemoryUsage(pair.first) +
276 EstimateItemMemoryUsage(pair.second);
277 }
278
279 // std::vector
280
281 template <class T, class A>
282 size_t EstimateMemoryUsage(const std::vector<T, A>& vector) {
283 return sizeof(T) * vector.capacity() + EstimateIterableMemoryUsage(vector);
284 }
285
286 // std::list
287
288 template <class T, class A>
289 size_t EstimateMemoryUsage(const std::list<T, A>& list) {
290 using value_type = typename std::list<T, A>::value_type;
291 struct Node {
292 Node* prev;
293 Node* next;
294 value_type value;
295 };
296 return sizeof(Node) * list.size() +
297 EstimateIterableMemoryUsage(list);
298 }
299
300 // Tree containers
301
302 template <class V>
303 size_t EstimateTreeMemoryUsage(size_t size) {
304 // Tree containers are modeled after libc++
305 // (__tree_node from include/__tree)
306 struct Node {
307 Node* left;
308 Node* right;
309 Node* parent;
310 bool is_black;
311 V value;
312 };
313 return sizeof(Node) * size;
314 }
315
316 template <class T, class C, class A>
317 size_t EstimateMemoryUsage(const std::set<T, C, A>& set) {
318 using value_type = typename std::set<T, C, A>::value_type;
319 return EstimateTreeMemoryUsage<value_type>(set.size()) +
320 EstimateIterableMemoryUsage(set);
321 }
322
323 template <class T, class C, class A>
324 size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set) {
325 using value_type = typename std::multiset<T, C, A>::value_type;
326 return EstimateTreeMemoryUsage<value_type>(set.size()) +
327 EstimateIterableMemoryUsage(set);
328 }
329
330 template <class K, class V, class C, class A>
331 size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map) {
332 using value_type = typename std::map<K, V, C, A>::value_type;
333 return EstimateTreeMemoryUsage<value_type>(map.size()) +
334 EstimateIterableMemoryUsage(map);
335 }
336
337 template <class K, class V, class C, class A>
338 size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map) {
339 using value_type = typename std::multimap<K, V, C, A>::value_type;
340 return EstimateTreeMemoryUsage<value_type>(map.size()) +
341 EstimateIterableMemoryUsage(map);
342 }
343
344 // HashMap containers
345
346 namespace internal {
347
348 // While hashtable containers model doesn't depend on STL implementation, one
349 // detail still crept in: bucket_count. It's used in size estimation, but its
350 // value after inserting N items is not predictable.
351 // This function is specialized by unittests to return constant value, thus
352 // excluding bucket_count from testing.
353 template <class V>
354 size_t HashMapBucketCountForTesting(size_t bucket_count) {
355 return bucket_count;
356 }
357
358 } // namespace internal
359
360 template <class V>
361 size_t EstimateHashMapMemoryUsage(size_t bucket_count, size_t size) {
362 // Hashtable containers are modeled after libc++
363 // (__hash_node from include/__hash_table)
364 struct Node {
365 void* next;
366 size_t hash;
367 V value;
368 };
369 using Bucket = void*;
370 bucket_count = internal::HashMapBucketCountForTesting<V>(bucket_count);
371 return sizeof(Bucket) * bucket_count + sizeof(Node) * size;
372 }
373
374 template <class K, class H, class KE, class A>
375 size_t EstimateMemoryUsage(const std::unordered_set<K, H, KE, A>& set) {
376 using value_type = typename std::unordered_set<K, H, KE, A>::value_type;
377 return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
378 set.size()) +
379 EstimateIterableMemoryUsage(set);
380 }
381
382 template <class K, class H, class KE, class A>
383 size_t EstimateMemoryUsage(const std::unordered_multiset<K, H, KE, A>& set) {
384 using value_type = typename std::unordered_multiset<K, H, KE, A>::value_type;
385 return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
386 set.size()) +
387 EstimateIterableMemoryUsage(set);
388 }
389
390 template <class K, class V, class H, class KE, class A>
391 size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map) {
392 using value_type = typename std::unordered_map<K, V, H, KE, A>::value_type;
393 return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
394 map.size()) +
395 EstimateIterableMemoryUsage(map);
396 }
397
398 template <class K, class V, class H, class KE, class A>
399 size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map) {
400 using value_type =
401 typename std::unordered_multimap<K, V, H, KE, A>::value_type;
402 return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
403 map.size()) +
404 EstimateIterableMemoryUsage(map);
405 }
406
407 } // namespace trace_event
408 } // namespace base
409
410 #endif // BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
OLDNEW
« no previous file with comments | « base/BUILD.gn ('k') | base/trace_event/memory_usage_estimator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698