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

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

Issue 2440393002: [tracing] Implement composable memory usage estimators. (Closed)
Patch Set: Simplify tests 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
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
Primiano Tucci (use gerrit) 2016/11/08 14:09:16 ?? Why this doesn't work if you just say base::tra
DmitrySkiba 2016/11/08 17:47:14 It won't work if EstimateMemoryUsage() for T is de
dcheng 2016/11/08 19:31:48 FWIW, if we can figure out some way to avoid 'usin
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)
Primiano Tucci (use gerrit) 2016/11/08 14:09:16 out of curiosity isn't this always size_t? Why do
DmitrySkiba 2016/11/08 17:47:14 The trick here is that if T doesn't have EMU membe
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);
dcheng 2016/11/08 19:31:48 Note that unique_ptr has a custom delete parameter
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>::value is true iff EstimateMemoryUsage(T) is available.
147 // (This is the default version, which is false.)
148 template <class T, class X = void>
149 struct HasEMU : std::false_type {};
150
151 // This HasEMU specialization is only picked up if there exists function
152 // EstimateMemoryUsage(const T&) that returns size_t. Simpler ways to
153 // achieve this don't work on MSVC.
154 template <class T>
155 struct HasEMU<
156 T,
157 typename std::enable_if<std::is_same<
158 size_t,
159 decltype(EstimateMemoryUsage(std::declval<const T&>()))>::value>::type>
160 : std::true_type {};
161
162 // EMUCaller<T> does three things:
163 // 1. Defines Call() method that calls EstimateMemoryUsage(T) if it's
164 // available.
165 // 2. If EstimateMemoryUsage(T) is not available, but T has trivial dtor
166 // (i.e. it's POD, integer, pointer, enum, etc.) then it defines Call()
167 // method that returns 0. This is useful for containers, which allocate
168 // memory regardless of T (also for cases like std::map<int, MyClass>).
169 // 3. Finally, if EstimateMemoryUsage(T) is not available, then it triggers
170 // a static_assert with a helpful message. That cuts numbers of errors
171 // considerably - if you just call EstimateMemoryUsage(T) but it's not
172 // available for T, then compiler will helpfully list *all* possible
173 // variants of it, with an explanation for each.
174 template <class T, class X = void>
175 struct EMUCaller {
176 // std::is_same<> below is only to make static_assert depend on T
177 // to force compilers to include type of T in the error message.
dcheng 2016/11/08 19:31:48 clang and msvc will both tell you which template i
178 static_assert(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&) { return 0; }
184 };
185
186 template <class T>
187 struct EMUCaller<T, typename std::enable_if<HasEMU<T>::value>::type> {
188 static size_t Call(const T& value) { return EstimateMemoryUsage(value); }
189 };
190
191 template <class T>
192 struct EMUCaller<
193 T,
194 typename std::enable_if<!HasEMU<T>::value &&
195 is_trivially_destructible<T>::value>::type> {
196 static size_t Call(const T& value) { return 0; }
197 };
198
199 } // namespace internal
200
201 // Proxy that deducts T and calls EMUCaller<T>.
202 // To be used by EstimateMemoryUsage() implementations for containers.
203 template <class T>
204 size_t EstimateItemMemoryUsage(const T& value) {
205 return internal::EMUCaller<T>::Call(value);
206 }
207
208 template <class I>
209 size_t EstimateIterableMemoryUsage(const I& iterable) {
210 size_t memory_usage = 0;
211 for (const auto& item : iterable) {
212 memory_usage += EstimateItemMemoryUsage(item);
213 }
214 return memory_usage;
215 }
216
217 // Global EstimateMemoryUsage(T) that just calls T::EstimateMemoryUsage().
218 template <class T>
219 auto EstimateMemoryUsage(const T& object)
220 -> decltype(object.EstimateMemoryUsage()) {
221 static_assert(
222 std::is_same<decltype(object.EstimateMemoryUsage()), size_t>::value,
223 "'T::EstimateMemoryUsage() const' must return size_t.");
224 return object.EstimateMemoryUsage();
225 }
226
227 // String
228
229 template <class C, class T, class A>
230 size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string) {
231 using string_type = std::basic_string<C, T, A>;
232 using value_type = typename string_type::value_type;
233 // C++11 doesn't leave much room for implementors - std::string can
234 // use short string optimization, but that's about it. We detect SSO
235 // by checking that c_str() points inside |string|.
236 const char* cstr = reinterpret_cast<const char*>(string.c_str());
237 const char* inline_cstr = reinterpret_cast<const char*>(&string);
238 if (cstr >= inline_cstr && cstr < inline_cstr + sizeof(string)) {
239 // SSO string
240 return 0;
241 }
242 return (string.capacity() + 1) * sizeof(value_type);
243 }
244
245 // Arrays
246
247 template <class T, size_t N>
248 size_t EstimateMemoryUsage(const std::array<T, N>& array) {
249 return EstimateIterableMemoryUsage(array);
250 }
251
252 template <class T, size_t N>
253 size_t EstimateMemoryUsage(T (&array)[N]) {
254 return EstimateIterableMemoryUsage(array);
255 }
256
257 template <class T>
258 size_t EstimateMemoryUsage(const T* array, size_t array_length) {
259 size_t memory_usage = sizeof(T) * array_length;
260 for (size_t i = 0; i != array_length; ++i) {
261 memory_usage += EstimateItemMemoryUsage(array[i]);
262 }
263 return memory_usage;
264 }
265
266 // std::unique_ptr
267
268 template <class T>
269 size_t EstimateMemoryUsage(const std::unique_ptr<T>& ptr) {
270 return ptr ? (sizeof(T) + EstimateItemMemoryUsage(*ptr)) : 0;
271 }
272
273 template <class T>
274 size_t EstimateMemoryUsage(const std::unique_ptr<T[]>& array,
275 size_t array_length) {
276 return EstimateMemoryUsage(array.get(), array_length);
277 }
278
279 // std::pair
280
281 template <class F, class S>
282 size_t EstimateMemoryUsage(const std::pair<F, S>& pair) {
283 return EstimateItemMemoryUsage(pair.first) +
284 EstimateItemMemoryUsage(pair.second);
285 }
286
287 // std::vector
288
289 template <class T, class A>
290 size_t EstimateMemoryUsage(const std::vector<T, A>& vector) {
291 return sizeof(T) * vector.capacity() + EstimateIterableMemoryUsage(vector);
292 }
293
294 // std::list
295
296 template <class T, class A>
297 size_t EstimateMemoryUsage(const std::list<T, A>& list) {
298 using value_type = typename std::list<T, A>::value_type;
299 struct Node {
300 Node* prev;
301 Node* next;
302 value_type value;
303 };
304 return sizeof(Node) * list.size() +
305 EstimateIterableMemoryUsage(list);
306 }
307
308 // Tree containers
309
310 template <class V>
311 size_t EstimateTreeMemoryUsage(size_t size) {
312 // Tree containers are modeled after libc++
313 // (__tree_node from include/__tree)
314 struct Node {
315 Node* left;
316 Node* right;
317 Node* parent;
318 bool is_black;
319 V value;
320 };
321 return sizeof(Node) * size;
322 }
323
324 template <class T, class C, class A>
325 size_t EstimateMemoryUsage(const std::set<T, C, A>& set) {
326 using value_type = typename std::set<T, C, A>::value_type;
327 return EstimateTreeMemoryUsage<value_type>(set.size()) +
328 EstimateIterableMemoryUsage(set);
329 }
330
331 template <class T, class C, class A>
332 size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set) {
333 using value_type = typename std::multiset<T, C, A>::value_type;
334 return EstimateTreeMemoryUsage<value_type>(set.size()) +
335 EstimateIterableMemoryUsage(set);
336 }
337
338 template <class K, class V, class C, class A>
339 size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map) {
340 using value_type = typename std::map<K, V, C, A>::value_type;
341 return EstimateTreeMemoryUsage<value_type>(map.size()) +
342 EstimateIterableMemoryUsage(map);
343 }
344
345 template <class K, class V, class C, class A>
346 size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map) {
347 using value_type = typename std::multimap<K, V, C, A>::value_type;
348 return EstimateTreeMemoryUsage<value_type>(map.size()) +
349 EstimateIterableMemoryUsage(map);
350 }
351
352 // HashMap containers
353
354 template <class V>
355 size_t EstimateHashMapMemoryUsage(size_t bucket_count, size_t size) {
356 // Hashtable containers are modeled after libc++
357 // (__hash_node from include/__hash_table)
358 struct Node {
359 void* next;
360 size_t hash;
361 V value;
362 };
363 using Bucket = void*;
364 return sizeof(Bucket) * bucket_count + sizeof(Node) * size;
365 }
366
367 template <class K, class H, class KE, class A>
368 size_t EstimateMemoryUsage(const std::unordered_set<K, H, KE, A>& set) {
369 using value_type = typename std::unordered_set<K, H, KE, A>::value_type;
370 return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
371 set.size()) +
372 EstimateIterableMemoryUsage(set);
373 }
374
375 template <class K, class H, class KE, class A>
376 size_t EstimateMemoryUsage(const std::unordered_multiset<K, H, KE, A>& set) {
377 using value_type = typename std::unordered_multiset<K, H, KE, A>::value_type;
378 return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
379 set.size()) +
380 EstimateIterableMemoryUsage(set);
381 }
382
383 template <class K, class V, class H, class KE, class A>
384 size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map) {
385 using value_type = typename std::unordered_map<K, V, H, KE, A>::value_type;
386 return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
387 map.size()) +
388 EstimateIterableMemoryUsage(map);
389 }
390
391 template <class K, class V, class H, class KE, class A>
392 size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map) {
393 using value_type =
394 typename std::unordered_multimap<K, V, H, KE, A>::value_type;
395 return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
396 map.size()) +
397 EstimateIterableMemoryUsage(map);
398 }
399
400 } // namespace trace_event
401 } // namespace base
402
403 #endif // BASE_TRACE_EVENT_ESTIMATE_MEMORY_USAGE_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698