Line data Source code
1 : // SPDX-License-Identifier: Apache-2.0
2 : /**
3 : * Copyright (C) 2020 Jihoon Lee <jhoon.it.lee@samsung.com>
4 : *
5 : * @file profiler.h
6 : * @date 09 December 2020
7 : * @brief Profiler related codes to be used to benchmark things
8 : * @see https://github.com/nnstreamer/nntrainer
9 : * @author Jihoon Lee <jhoon.it.lee@samsung.com>
10 : * @bug No known bugs except for NYI items
11 : *
12 : */
13 : #ifndef __PROFILER_H__
14 : #define __PROFILER_H__
15 :
16 : #include <chrono>
17 : #include <future>
18 : #include <iosfwd>
19 : #include <list>
20 : #include <mutex>
21 : #include <set>
22 : #include <string>
23 : #include <unordered_map>
24 : #include <unordered_set>
25 : #include <vector>
26 :
27 : #include "singleton.h"
28 :
29 : using timepoint = std::chrono::time_point<std::chrono::steady_clock>;
30 :
31 : #ifndef PROFILE
32 :
33 : #define PROFILE_TIME_START(event_key)
34 : #define PROFILE_TIME_END(event_key)
35 : #define PROFILE_TIME_REGISTER_EVENT(event_key, event_str)
36 : #define PROFILE_MEM_ALLOC(ptr, size, str)
37 : #define PROFILE_MEM_DEALLOC(ptr)
38 : #define PROFILE_CACHE_ALLOC(ptr, size, str, policy, fsu)
39 : #define PROFILE_CACHE_DEALLOC(ptr, policy, fsu)
40 : #define PROFILE_BEGIN(listener)
41 : #define PROFILE_END(listener)
42 : #define PROFILE_MEM_ANNOTATE(str)
43 :
44 : #else /** PROFILE */
45 :
46 : #define PROFILE_TIME_START(event_key) \
47 : nntrainer::profile::Profiler::Global().start(event_key)
48 :
49 : #define PROFILE_TIME_END(event_key) \
50 : nntrainer::profile::Profiler::Global().end(event_key)
51 :
52 : #define PROFILE_TIME_REGISTER_EVENT(event_key, event_str) \
53 : do { \
54 : event_key = \
55 : nntrainer::profile::Profiler::Global().registerTimeItem(event_str); \
56 : } while (0)
57 :
58 : #define PROFILE_MEM_ALLOC(ptr, size, str) \
59 : nntrainer::profile::Profiler::Global().alloc(ptr, size, str)
60 :
61 : #define PROFILE_MEM_DEALLOC(ptr) \
62 : nntrainer::profile::Profiler::Global().dealloc(ptr)
63 :
64 : #define PROFILE_CACHE_ALLOC(ptr, size, str, policy, fsu) \
65 : nntrainer::profile::Profiler::Global().alloc(ptr, size, str, policy, fsu)
66 :
67 : #define PROFILE_CACHE_DEALLOC(ptr, policy, fsu) \
68 : nntrainer::profile::Profiler::Global().dealloc(ptr, policy, fsu)
69 :
70 : #define PROFILE_BEGIN(listener) \
71 : do { \
72 : nntrainer::profile::Profiler::Global().subscribe(listener); \
73 : } while (0)
74 :
75 : #define PROFILE_END(listener) \
76 : do { \
77 : std::cout << *listener; \
78 : } while (0)
79 :
80 : #define PROFILE_MEM_ANNOTATE(str) \
81 : nntrainer::profile::Profiler::Global().annotate(str)
82 :
83 : #endif /** PROFILE */
84 :
85 : namespace nntrainer {
86 :
87 : namespace profile {
88 :
89 : enum PROFILE_EVENT {
90 : EVENT_TIME_START = 0,
91 : EVENT_TIME_END = 1,
92 : EVENT_MEM_ALLOC = 2,
93 : EVENT_MEM_DEALLOC = 3,
94 : EVENT_MEM_ANNOTATE = 4,
95 : };
96 :
97 : /**
98 : * @brief Data for each profile event
99 : *
100 : */
101 0 : struct ProfileEventData {
102 : public:
103 : /**
104 : * @brief Construct a new ProfileEventData struct
105 : *
106 : */
107 0 : ProfileEventData(int item, size_t cur, size_t total, std::string str,
108 0 : std::chrono::microseconds dur) :
109 0 : time_item(item),
110 0 : alloc_current(cur),
111 0 : alloc_total(total),
112 0 : cache_fsu(false),
113 0 : event_str(str),
114 0 : duration(dur) {}
115 :
116 : /**
117 : * @brief Construct a new ProfileEventData struct
118 : *
119 : */
120 0 : ProfileEventData(int item, size_t cur, size_t total, std::string str,
121 : std::chrono::microseconds dur, std::string policy,
122 0 : bool fsu) :
123 0 : time_item(item),
124 0 : alloc_current(cur),
125 0 : alloc_total(total),
126 0 : cache_policy(policy),
127 0 : cache_fsu(fsu),
128 0 : event_str(str),
129 0 : duration(dur) {}
130 :
131 : /* for time profile */
132 : int time_item;
133 :
134 : /* current allocation size */
135 : size_t alloc_current;
136 :
137 : /* total allocation size */
138 : size_t alloc_total;
139 :
140 : std::string cache_policy;
141 : bool cache_fsu;
142 :
143 : /* common data */
144 : std::string event_str;
145 : std::chrono::microseconds duration;
146 : };
147 :
148 : class Profiler;
149 :
150 : /**
151 : * @brief Generic profile listener class to attach to a profiler,
152 : * this can be inherited to create a custom profile listener
153 : */
154 : class ProfileListener {
155 : public:
156 : /**
157 : * @brief Construct a new Profile Listener object
158 : *
159 : */
160 : explicit ProfileListener() = default;
161 :
162 : /**
163 : * @brief Destroy the Base Profile Listener object
164 : *
165 : */
166 : virtual ~ProfileListener() noexcept = default;
167 :
168 : /**
169 : * @brief A callback function to be called from a profiler
170 : *
171 : * @param event event type
172 : * @param data event data
173 : */
174 : virtual void notify(PROFILE_EVENT event,
175 : const std::shared_ptr<ProfileEventData> data) = 0;
176 :
177 : /**
178 : * @brief resets the listener to the inital state for a particular key
179 : *
180 : * @param time_item time item which will be reset
181 : */
182 : virtual void reset(const int time_item, const std::string &str) = 0;
183 :
184 : /**
185 : * @brief get the latest result of a event
186 : *
187 : * @param time_item time item to query the result
188 : * @return const std::chrono::microseconds
189 : */
190 : virtual const std::chrono::microseconds result(const int time_item) = 0;
191 :
192 : /**
193 : * @brief report the result
194 : *
195 : * @param out outstream object to make a report
196 : */
197 : virtual void report(std::ostream &out) const = 0;
198 : };
199 :
200 : /**
201 : * @brief Generic Profiler Listener
202 : *
203 : */
204 : class GenericProfileListener : public ProfileListener {
205 : public:
206 : /**
207 : * @brief Construct a new GenericProfile Listener object
208 : *
209 : * @param warmups_ ignore first @a warmups_ records when making time report
210 : */
211 : explicit GenericProfileListener(int warmups_ = 0) :
212 : ProfileListener(),
213 : start_time(std::chrono::steady_clock::now()),
214 : warmups(warmups_),
215 : mem_max(0),
216 : mem_sum(0),
217 : mem_average(0),
218 : mem_count(0) {}
219 :
220 : /**
221 : * @brief Destroy the Generic Profile Listener object
222 : *
223 : */
224 0 : virtual ~GenericProfileListener() = default;
225 :
226 : /**
227 : * @brief A callback function to be called from a profiler
228 : *
229 : * @param event event type
230 : * @param data event data
231 : */
232 : virtual void notify(PROFILE_EVENT event,
233 : const std::shared_ptr<ProfileEventData> data) override;
234 :
235 : /**
236 : * @copydoc ProfileListener::reset(const int time_item)
237 : */
238 : virtual void reset(const int time_item, const std::string &str) override;
239 :
240 : /**
241 : * @copydoc ProfileListener::result(const int event)
242 : */
243 : virtual const std::chrono::microseconds result(const int event) override;
244 :
245 : /**
246 : * @copydoc ProfileListener::report(std::ostream &out)
247 : */
248 : virtual void report(std::ostream &out) const override;
249 :
250 : private:
251 : /**
252 : * @brief Called when time event occurs
253 : *
254 : */
255 : void onNotifyTimeEvent(PROFILE_EVENT event, const int time_item,
256 : const std::string &str,
257 : const std::chrono::microseconds &duration);
258 :
259 : /**
260 : * @brief Called when memory event occurs
261 : *
262 : */
263 : void onNotifyMemoryEvent(PROFILE_EVENT event, const size_t alloc_current,
264 : const size_t alloc_total, const std::string &str,
265 : const std::chrono::microseconds &duration,
266 : const std::string &policy, bool fsu);
267 :
268 : std::chrono::time_point<std::chrono::steady_clock> start_time;
269 : unsigned int warmups;
270 :
271 : static constexpr int CUR = 0;
272 : static constexpr int MIN = 1;
273 : static constexpr int MAX = 2;
274 : static constexpr int SUM = 3;
275 : static constexpr int CNT = 4;
276 :
277 : std::unordered_map<int, std::tuple<std::chrono::microseconds, /** CUR */
278 : std::chrono::microseconds, /** MIN */
279 : std::chrono::microseconds, /** MAX */
280 : std::chrono::microseconds, /** SUM */
281 : unsigned int /** CNT */>>
282 : time_taken;
283 :
284 : std::list<std::tuple<PROFILE_EVENT, size_t, size_t, std::string,
285 : std::chrono::microseconds, std::string, bool>>
286 : mem_taken; /**< taken memory information <event, current, total, str, dur,
287 : policy, fsu> */
288 : size_t mem_max; /**< memory max size */
289 : size_t mem_sum; /**< memory sum */
290 : size_t mem_average; /**< memory average */
291 : size_t mem_count; /**< memory count */
292 :
293 : std::unordered_map<int, std::string> names;
294 : };
295 :
296 : /**
297 : * @brief Overriding output stream for layers and it's derived class
298 : */
299 : template <typename T,
300 : typename std::enable_if_t<std::is_base_of<ProfileListener, T>::value,
301 : T> * = nullptr>
302 : std::ostream &operator<<(std::ostream &out, T &l) {
303 : l.report(out);
304 : return out;
305 : }
306 :
307 : /**
308 : * @brief Profiler object
309 : *
310 : */
311 : class Profiler : public Singleton<Profiler> {
312 : public:
313 : /**
314 : * @brief start time profile
315 : *
316 : * @param time_item time item to be recorded
317 : */
318 : void start(const int time_item);
319 :
320 : /**
321 : * @brief end time profile and notify to the listeners
322 : *
323 : * @param time_item time item to be finished
324 : */
325 : void end(const int time_item);
326 :
327 : /**
328 : * @brief trace memory allocation
329 : *
330 : * @param ptr allocated memory pointer
331 : * @param size amount of allocated memory
332 : * @param str information string
333 : */
334 : void alloc(const void *ptr, size_t size, const std::string &str,
335 : const std::string &policy = "", bool fsu = false);
336 :
337 : /**
338 : * @brief trace memory de-allocation
339 : *
340 : * @param ptr de-allocated memory pointer
341 : */
342 : void dealloc(const void *ptr, const std::string &policy = "",
343 : bool fsu = false);
344 :
345 : /**
346 : * @brief add annotation on memory profile data
347 : *
348 : * @param str annotate message
349 : */
350 : void annotate(const std::string &str);
351 :
352 : /**
353 : * @brief subscribe a listener to the profiler
354 : *
355 : * @param listener listener to register, listener must call unsubscribe on
356 : * destruction
357 : * @param events event listeners are subscribing, if empty listener subscribes
358 : * to all events
359 : * @throw std::invalid_argument if listener is already registered
360 : */
361 : void subscribe(std::shared_ptr<ProfileListener> listener,
362 : const std::set<int> &time_item = {});
363 :
364 : /**
365 : * @brief unsubscribe a listener from the profiler
366 : *
367 : * @param listener listener to unsubscribe
368 : */
369 : void unsubscribe(std::shared_ptr<ProfileListener> listener);
370 :
371 : /**
372 : * @brief registerEvent to record.
373 : * @note Call to the function shouldn't be inside a critical path
374 : *
375 : * @return int return NEGATIVE integer to distinguish from reserved events
376 : */
377 : int registerTimeItem(const std::string &name);
378 :
379 : private:
380 : /**
381 : * @brief notify the result
382 : *
383 : * @param event event to notify
384 : * @param value measured value from the profiler
385 : */
386 : void notifyListeners(PROFILE_EVENT event,
387 : const std::shared_ptr<ProfileEventData> data);
388 :
389 : std::unordered_set<std::shared_ptr<ProfileListener>>
390 : listeners; /**< event listeners */
391 :
392 : std::unordered_map<int, std::string>
393 : time_item_names; /**< registered item names (time_item, string) */
394 : std::unordered_map<int, timepoint>
395 : time_item_times; /**< registered time items (time_item, time) */
396 : std::unordered_map<int, std::set<std::shared_ptr<ProfileListener>>>
397 : time_item_listeners;
398 : /**< registered listeners for each itemtems (time_item, listeners) */
399 :
400 : std::unordered_map<const void *, std::tuple<size_t, timepoint, std::string>>
401 : allocates; /**< allocated memory information (ptr, (size, time, info) */
402 :
403 : std::atomic<std::size_t> total_size = 0; /**< total allocated memory size */
404 :
405 : std::mutex listeners_mutex; /**< protect listeners */
406 : std::mutex allocates_mutex; /**< protect allocates */
407 : std::mutex registr_mutex; /**< protect custom event registration */
408 : };
409 :
410 : } // namespace profile
411 :
412 : } // namespace nntrainer
413 :
414 : #endif /** __PROFILER_H__ */
|