HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
timing.h
Go to the documentation of this file.
1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the terms set forth in the LICENSE.txt file available at
5 // https://openusd.org/license.
6 //
7 #ifndef PXR_BASE_ARCH_TIMING_H
8 #define PXR_BASE_ARCH_TIMING_H
9 
10 /// \file arch/timing.h
11 /// \ingroup group_arch_SystemFunctions
12 /// High-resolution, low-cost timing routines.
13 
14 #include "pxr/pxr.h"
15 #include "pxr/base/arch/api.h"
16 #include "pxr/base/arch/defines.h"
17 #include "pxr/base/arch/inttypes.h"
18 
19 /// \addtogroup group_arch_SystemFunctions
20 ///@{
21 
22 // See if we should use the x86 TSC register for timing.
23 #if defined(PXR_ARCH_PREFER_TSC_TIMING) && \
24  defined(ARCH_OS_LINUX) && \
25  defined(ARCH_CPU_INTEL) && \
26  (defined(ARCH_COMPILER_CLANG) || defined(ARCH_COMPILER_GCC))
27 #define ARCH_USE_TSC_TIMING 1
28 #else
29 #define ARCH_USE_TSC_TIMING 0
30 #endif
31 
32 #if ARCH_USE_TSC_TIMING
33 #include <x86intrin.h>
34 #endif
35 
36 #include <atomic>
37 #include <chrono>
38 #include <cmath>
39 
40 // XXX: None of <algorithm>, <iterator>, nor <numeric> are used by the timing
41 // routines, but the includes have been here forever and removing them causes
42 // other code that is not including all the headers it needs to fail to compile.
43 // These should be deprecated and removed.
44 #include <algorithm>
45 #include <iterator>
46 #include <numeric>
47 
49 
50 // Use a std::chrono clock if we're not using the Intel RDTSC instruction
51 #if !ARCH_USE_TSC_TIMING
52 /// The std::chrono clock to use to measure time.
53 ///
54 /// std::chrono::steady_clock appears to be fast and accurate on all of our
55 /// supported systems.
56 using Arch_TimingClock = std::chrono::steady_clock;
57 #endif
58 
59 /// Return the current time in system-dependent units.
60 ///
61 /// The current time is returned as a number of "ticks", where each tick
62 /// represents some system-dependent amount of time. The resolution of the
63 /// timing routines varies, but on all systems, it is well under one
64 /// microsecond. The cost of this routine is in the 10s-to-100s of nanoseconds
65 /// on GHz class machines.
66 inline uint64_t
68 {
69 #if ARCH_USE_TSC_TIMING
70  return __rdtsc();
71 #else
72  return Arch_TimingClock::now().time_since_epoch().count();
73 #endif
74 }
75 
76 
77 /// Get a "start" tick time for measuring an interval of time, followed by a
78 /// later call to ArchGetStopTickTime(). Or see ArchIntervalTimer. This is
79 /// like ArchGetTickTime but it includes compiler & CPU fencing & reordering
80 /// constraints in an attempt to get the best measurement possible.
81 inline uint64_t
83 {
84  uint64_t t;
85 
86 #if ARCH_USE_TSC_TIMING
87 
88  // Prevent reorders by the compiler.
89  std::atomic_signal_fence(std::memory_order_seq_cst);
90  asm volatile(
91  "lfence\n\t"
92  "rdtsc\n\t"
93  "shl $32, %%rdx\n\t"
94  "or %%rdx, %0\n\t"
95  "lfence"
96  : "=a"(t)
97  :
98  // rdtsc writes rdx
99  // shl modifies cc flags
100  : "rdx", "cc");
101 
102 #else
103 
104  std::atomic_signal_fence(std::memory_order_seq_cst);
105  t = ArchGetTickTime();
106  std::atomic_signal_fence(std::memory_order_seq_cst);
107 
108 #endif
109 
110  return t;
111 }
112 
113 /// Get a "stop" tick time for measuring an interval of time. See
114 /// ArchGetStartTickTime() or ArchIntervalTimer. This is like ArchGetTickTime
115 /// but it includes compiler & CPU fencing & reordering constraints in an
116 /// attempt to get the best measurement possible.
117 inline uint64_t
119 {
120  uint64_t t;
121 
122 #if ARCH_USE_TSC_TIMING
123 
124  // Prevent reorders by the compiler.
125  std::atomic_signal_fence(std::memory_order_seq_cst);
126  asm volatile(
127  "rdtscp\n\t"
128  "shl $32, %%rdx\n\t"
129  "or %%rdx, %0\n\t"
130  "lfence"
131  : "=a"(t)
132  :
133  // rdtscp writes rcx & rdx
134  // shl modifies cc flags
135  : "rcx", "rdx", "cc");
136 
137 #else
138 
139  std::atomic_signal_fence(std::memory_order_seq_cst);
140  t = ArchGetTickTime();
141  std::atomic_signal_fence(std::memory_order_seq_cst);
142 
143 #endif
144 
145  return t;
146 }
147 
148 /// A simple timer class for measuring an interval of time using the
149 /// ArchTickTimer facilities.
151 {
152  explicit ArchIntervalTimer(bool start=true)
153  : _started(start) {
154  if (_started) {
155  _startTicks = ArchGetStartTickTime();
156  }
157  }
158 
159  void Start() {
160  _started = true;
161  _startTicks = ArchGetStartTickTime();
162  }
163 
164  bool IsStarted() const {
165  return _started;
166  }
167 
168  uint64_t GetStartTicks() const {
169  return _startTicks;
170  }
171 
172  uint64_t GetCurrentTicks() {
173  return ArchGetStopTickTime();
174  }
175 
176  uint64_t GetElapsedTicks() {
177  if (!_started) {
178  return 0;
179  }
180  return ArchGetStopTickTime() - _startTicks;
181  }
182 private:
183  bool _started = false;
184  uint64_t _startTicks;
185 };
186 
187 /// Return the tick time resolution. Although the number of ticks per second
188 /// may be very large, on many current systems the tick timers do not update at
189 /// that rate. Rather, sequential calls to ArchGetTickTime() may report
190 /// increases of 10s to 100s of ticks, with a minimum increment betwewen calls.
191 /// This function returns that minimum increment as measured at startup time.
192 ///
193 /// Note that if this value is of sufficient size, then short times measured
194 /// with tick timers are potentially subject to significant noise. In
195 /// particular, an interval of measured tick time is liable to be off by +/- one
196 /// ArchGetTickQuantum().
197 ARCH_API
198 uint64_t ArchGetTickQuantum();
199 
200 /// Return the ticks taken to record an interval of time with ArchIntervalTimer,
201 /// as measured at startup time.
202 ARCH_API
204 
205 
206 /// Get nanoseconds per tick. Useful when converting ticks obtained from
207 /// \c ArchTickTime()
208 #if defined(doxygen) || ARCH_USE_TSC_TIMING
209 ARCH_API
211 #else
213 {
214  return 1e+9 * Arch_TimingClock::period::num / Arch_TimingClock::period::den;
215 }
216 #endif
217 /// Convert a duration measured in "ticks", as returned by
218 /// \c ArchGetTickTime(), to nanoseconds.
219 ///
220 /// An example to test the timing routines would be:
221 /// \code
222 /// ArchIntervalTimer iTimer;
223 /// sleep(10);
224 ///
225 /// // duration should be approximately 10 * 1e9 = 1e10 nanoseconds.
226 /// int64_t duration = ArchTicksToNanoseconds(iTimer.GetElapsedTicks());
227 /// \endcode
228 ///
229 #if defined(doxygen) || ARCH_USE_TSC_TIMING
230 ARCH_API
231 int64_t ArchTicksToNanoseconds(uint64_t nTicks);
232 #else
233 inline int64_t ArchTicksToNanoseconds(uint64_t nTicks)
234 {
235  return static_cast<int64_t>(
236  std::llround(nTicks * ArchGetNanosecondsPerTick()));
237 }
238 #endif
239 
240 /// Convert a duration measured in "ticks", as returned by
241 /// \c ArchGetTickTime(), to seconds.
242 #if defined(doxygen) || ARCH_USE_TSC_TIMING
243 ARCH_API
244 double ArchTicksToSeconds(uint64_t nTicks);
245 #else
246 inline double ArchTicksToSeconds(uint64_t nTicks)
247 {
248  return nTicks * ArchGetNanosecondsPerTick() / 1e+9;
249 }
250 #endif
251 
252 /// Convert a duration in seconds to "ticks", as returned by
253 /// \c ArchGetTickTime().
254 #if defined(doxygen) || ARCH_USE_TSC_TIMING
255 ARCH_API
256 uint64_t ArchSecondsToTicks(double seconds);
257 #else
258 inline uint64_t ArchSecondsToTicks(double seconds)
259 {
260  return seconds * 1e+9 / ArchGetNanosecondsPerTick();
261 }
262 #endif
263 
264 ARCH_API
265 uint64_t
266 Arch_MeasureExecutionTime(uint64_t maxTicks, bool *reachedConsensus,
267  void const *m, uint64_t (*callM)(void const *, int));
268 
269 /// Run \p fn repeatedly attempting to determine a consensus fastest execution
270 /// time with low noise, for up to \p maxTicks, then return the consensus
271 /// fastest execution time. If a consensus is not reached in that time, return
272 /// a best estimate instead. If \p reachedConsensus is not null, set it to
273 /// indicate whether or not a consensus was reached. This function ignores \p
274 /// maxTicks greater than 5 billion ticks and runs for up to 5 billion ticks
275 /// instead. The \p fn will run for an indeterminate number of times, so it
276 /// should be side-effect free. Also, it should do essentially the same work
277 /// on every invocation so that timing its execution makes sense.
278 template <class Fn>
279 uint64_t
281  Fn const &fn,
282  uint64_t maxTicks = 1e7,
283  bool *reachedConsensus = nullptr)
284 {
285  auto measureN = [&fn](int nTimes) -> uint64_t {
286  ArchIntervalTimer iTimer;
287  for (int i = nTimes; i--; ) {
288  std::atomic_signal_fence(std::memory_order_seq_cst);
289  (void)fn();
290  std::atomic_signal_fence(std::memory_order_seq_cst);
291  }
292  return iTimer.GetElapsedTicks();
293  };
294 
295  using MeasureNType = decltype(measureN);
296 
298  maxTicks, reachedConsensus,
299  static_cast<void const *>(&measureN),
300  [](void const *mN, int nTimes) {
301  return (*static_cast<MeasureNType const *>(mN))(nTimes);
302  });
303 }
304 
305 ///@}
306 
308 
309 #endif // PXR_BASE_ARCH_TIMING_H
double ArchGetNanosecondsPerTick()
Definition: timing.h:212
void
Definition: png.h:1083
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
GLuint start
Definition: glcorearb.h:475
uint64_t ArchGetTickTime()
Definition: timing.h:67
bool IsStarted() const
Definition: timing.h:164
int64_t ArchTicksToNanoseconds(uint64_t nTicks)
Definition: timing.h:233
ARCH_API uint64_t ArchGetIntervalTimerTickOverhead()
uint64_t GetElapsedTicks()
Definition: timing.h:176
double ArchTicksToSeconds(uint64_t nTicks)
Definition: timing.h:246
std::chrono::steady_clock Arch_TimingClock
Definition: timing.h:56
uint64_t GetCurrentTicks()
Definition: timing.h:172
ArchIntervalTimer(bool start=true)
Definition: timing.h:152
uint64_t GetStartTicks() const
Definition: timing.h:168
uint64_t ArchMeasureExecutionTime(Fn const &fn, uint64_t maxTicks=1e7, bool *reachedConsensus=nullptr)
Definition: timing.h:280
GLdouble t
Definition: glad.h:2397
void Start()
Definition: timing.h:159
uint64_t ArchGetStartTickTime()
Definition: timing.h:82
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
#define ARCH_API
Definition: api.h:23
uint64_t ArchGetStopTickTime()
Definition: timing.h:118
uint64_t ArchSecondsToTicks(double seconds)
Definition: timing.h:258
ARCH_API uint64_t Arch_MeasureExecutionTime(uint64_t maxTicks, bool *reachedConsensus, void const *m, uint64_t(*callM)(void const *, int))
ARCH_API uint64_t ArchGetTickQuantum()