Turi Create  4.0
tracepoint.hpp
1 /* Copyright © 2017 Apple Inc. All rights reserved.
2  *
3  * Use of this source code is governed by a BSD-3-clause license that can
4  * be found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
5  */
6 #ifndef TURI_UTIL_TRACEPOINT_HPP
7 #define TURI_UTIL_TRACEPOINT_HPP
8 #include <iostream>
9 #include <vector>
10 #include <string>
11 #include <timer/timer.hpp>
12 #include <core/util/branch_hints.hpp>
13 #include <core/parallel/atomic.hpp>
14 #include <core/parallel/atomic_ops.hpp>
15 
16 /**
17  * \defgroup perfmonitoring Intrusive Performance Monitoring
18  * \brief A tracepoint utility that provides intrusive (requires code change) performance monitoring.
19  *
20  * The tracepoint utility provides a extremely low overhead way of profiling a
21  * section of code, counting the number of times the section is entered, the
22  * average, maximum and mimimum runtimes of the section.
23  *
24  * The tracepoint utility can be enabled file by file, or in an entire project by
25  * setting the USE_TRACEPOINT macro before including tracepoint.hpp, or
26  * predefining USE_TRACEPOINT globally.
27  * \code
28  * #define USE_TRACEPOINT
29  * #include "perf/tracepoint.hpp"
30  * \endcode
31  *
32  * Example Usage:
33  * \code
34  * DECLARE_TRACER(event)
35  * INITIALIZE_TRACER(event, "event counter name");
36  * Then later on...
37  * BEGIN_TRACEPOINT(event)
38  * ... Do stuff ...
39  * END_TRACEPOINT(event)
40  * \endcode
41  * See \ref DECLARE_TRACER for details.
42  */
43 
44 namespace turi{
45 /**
46  * \ingroup perfmonitoring
47  * Implementation detail of the tracing macros.
48  */
49 struct trace_count{
50  std::string name;
51  std::string description;
52  bool print_on_destruct;
53  turi::atomic<unsigned long long> count;
54  turi::atomic<unsigned long long> total;
55  unsigned long long minimum;
56  unsigned long long maximum;
57  inline trace_count(std::string name = "",
58  std::string description = "",
59  bool print_on_destruct = true):
60  name(name),
61  description(description),
62  print_on_destruct(print_on_destruct),
63  count(0),
64  total(0),
65  minimum(std::numeric_limits<unsigned long long>::max()),
66  maximum(0) { }
67 
68  /**
69  * Initializes the tracer with a name, a description
70  * and whether to print on destruction
71  */
72  inline void initialize(std::string n,
73  std::string desc,
74  bool print_out = true) {
75  name = n;
76  description = desc;
77  print_on_destruct = print_out;
78  }
79 
80  /**
81  * Adds an event time to the trace
82  */
83  inline void incorporate(unsigned long long val) __attribute__((always_inline)) {
84  count.inc();
85  total.inc(val);
86  while(1) {
87  unsigned long long m = minimum;
88  if (__likely__(val > m || turi::atomic_compare_and_swap(minimum, m, val))) break;
89  }
90  while(1) {
91  unsigned long long m = maximum;
92  if (__likely__(val < m || turi::atomic_compare_and_swap(maximum, m, val))) break;
93  }
94  }
95 
96  /**
97  * Adds the counts in a second tracer to the current tracer.
98  */
99  inline void incorporate(const trace_count &val) __attribute__((always_inline)) {
100  count.inc(val.count.value);
101  total.inc(val.total.value);
102  while(1) {
103  unsigned long long m = minimum;
104  if (__likely__(val.minimum > m || turi::atomic_compare_and_swap(minimum, m, val.minimum))) break;
105  }
106  while(1) {
107  unsigned long long m = maximum;
108  if (__likely__(val.maximum < m || turi::atomic_compare_and_swap(maximum, m, val.maximum))) break;
109  }
110  }
111 
112  /**
113  * Adds the counts in a second tracer to the current tracer.
114  */
116  incorporate(val);
117  return *this;
118  }
119 
120  /**
121  * Destructor. Will print to cerr if initialize() is called
122  * with "true" as the 3rd argument
123  */
124  ~trace_count();
125 
126  /**
127  * Prints the tracer counts
128  */
129  void print(std::ostream& out, unsigned long long tpersec = 0) const;
130 };
131 
132 } // namespace
133 
134 
135 /**
136  * \ingroup perfmonitoring
137  * \def DECLARE_TRACER(name)
138  * Creates a tracing object with a given name. This creates a variable
139  * called "name" which is of type trace_count. and is equivalent to:
140  *
141  * turi::trace_count name;
142  *
143  * The primary reason to use this macro instead of just writing
144  * the code above directly, is that the macro is ignored and compiles
145  * to nothing when tracepoints are disabled.
146  *
147  * Example Usage:
148  * \code
149  * DECLARE_TRACER(event)
150  * INITIALIZE_TRACER(event, "event counter name");
151  * Then later on...
152  * BEGIN_TRACEPOINT(event)
153  * ... Do stuff ...
154  * END_TRACEPOINT(event)
155  * \endcode
156  *
157  * There are some minor caveats. BEGIN_TRACEPOINT really declares a variable,
158  * so you must begin and end a tracepoint within the same scope, and you cannot
159  * call BEGIN_TRACEPOINT on the same event within the same scope.
160  *
161  * At program termination the contents of the tracepoint will be emitted.
162  * It will print the number times the tracepoint is entered, the total
163  * program time spent in the tracepoint, as well as average, minimum and
164  * maximum time spent in the tracepoint. Note that the tracepoint
165  * is safe to use even when running in parallel. The total time spent in the
166  * tracepoint is hence the sum over all threads.
167  *
168  * The tracepoint uses rdtsc for fast clock cycle counting, hence might be
169  * inaccurate on systems where rdtsc is not necessarily monotonic.
170  */
171 
172 /**
173  * \ingroup perfmonitoring
174  * \def INITIALIZE_TRACER(name, desc)
175  * Initializes the tracer created by \ref DECLARE_TRACER with a description.
176  * The object with name "name" created by DECLARE_TRACER must be in scope.
177  * This initializes the tracer "name" with a description, and
178  * configures the tracer to print when the tracer "name" is destroyed.
179  *
180  *
181  */
182 
183 /**
184  * \ingroup perfmonitoring
185  * \def INITIALIZE_TRACER_NO_PRINT(name, desc)
186  * Initializes the tracer created by \ref DECLARE_TRACER with a description.
187  * The object with name "name" created by DECLARE_TRACER must be in scope.
188  * This initializes the tracer "name" with a description, and
189  * configures the tracer to NOT print when the tracer "name" is destroyed.
190  */
191 
192 /**
193  * \ingroup perfmonitoring
194  * \def BEGIN_TRACEPOINT(name)
195  * Begins a tracepoint.
196  * The object with name "name" created by DECLARE_TRACER must be in scope.
197  * Times a block of code. Every END_TRACEPOINT must be matched with a
198  * BEGIN_TRACEPOINT within the same scope. Tracepoints are safe to use in
199  * concurrent use.
200  */
201 
202 /**
203  * \ingroup perfmonitoring
204  * \def END_TRACEPOINT(name)
205  * Ends a tracepoint; see \ref BEGIN_TRACEPOINT.
206  */
207 
208 /**
209  * \ingroup perfmonitoring
210  * \def END_AND_BEGIN_TRACEPOINT(endname, beginname)
211  * Ends the tracepoint with the name "endname" and begins a tracepoint with
212  * name "beginname". Conceptually equivalent to
213  * \code
214  * END_TRACEPOINT(endname)
215  * BEGIN_TRACEPOINT(beginname)
216  * \endcode
217  * but with slightly less overhead.
218  *
219  */
220 
221 #ifdef DOXYGEN_DOCUMENTATION
222 #define USE_TRACEPOINT
223 #endif
224 #ifdef USE_TRACEPOINT
225 #define DECLARE_TRACER(name) turi::trace_count name;
226 
227 #define INITIALIZE_TRACER(name, description) name.initialize(#name, description);
228 #define INITIALIZE_TRACER_NO_PRINT(name, description) name.initialize(#name, description, false);
229 
230 #define BEGIN_TRACEPOINT(name) unsigned long long __ ## name ## _trace_ = rdtsc();
231 #define END_TRACEPOINT(name) name.incorporate(rdtsc() - __ ## name ## _trace_);
232 #define END_AND_BEGIN_TRACEPOINT(endname, beginname) unsigned long long __ ## beginname ## _trace_ = rdtsc(); \
233  endname.incorporate(__ ## beginname ## _trace_ - __ ## endname ## _trace_);
234 #else
235 #define DECLARE_TRACER(name)
236 #define INITIALIZE_TRACER(name, description)
237 #define INITIALIZE_TRACER_NO_PRINT(name, description)
238 
239 #define BEGIN_TRACEPOINT(name)
240 #define END_TRACEPOINT(name)
241 #define END_AND_BEGIN_TRACEPOINT(endname, beginname)
242 #endif
243 
244 #endif
void incorporate(unsigned long long val) __attribute__((always_inline))
Definition: tracepoint.hpp:83
void incorporate(const trace_count &val) __attribute__((always_inline))
Definition: tracepoint.hpp:99
#define __likely__(x)
void print(std::ostream &out, unsigned long long tpersec=0) const
bool atomic_compare_and_swap(T &a, T oldval, T newval)
Definition: atomic_ops.hpp:27
void initialize(std::string n, std::string desc, bool print_out=true)
Definition: tracepoint.hpp:72
trace_count & operator+=(trace_count &val)
Definition: tracepoint.hpp:115