Coverage Report

Created: 2025-07-12 07:43

/src/shaderc/third_party/spirv-tools/source/util/timer.h
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2018 Google LLC.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
// Contains utils for getting resource utilization
16
17
#ifndef SOURCE_UTIL_TIMER_H_
18
#define SOURCE_UTIL_TIMER_H_
19
20
#if defined(SPIRV_TIMER_ENABLED)
21
22
#include <sys/resource.h>
23
#include <cassert>
24
#include <iostream>
25
26
// A macro to call spvtools::utils::PrintTimerDescription(std::ostream*, bool).
27
// The first argument must be given as std::ostream*. If it is NULL, the
28
// function does nothing. Otherwise, it prints resource types measured by Timer
29
// class. The second is optional and if it is true, the function also prints
30
// resource type fields related to memory. Otherwise, it does not print memory
31
// related fields. Its default is false. In usual, this must be placed before
32
// calling Timer::Report() to inform what those fields printed by
33
// Timer::Report() indicate (or spvtools::utils::PrintTimerDescription() must be
34
// used instead).
35
#define SPIRV_TIMER_DESCRIPTION(...) \
36
1.80k
  spvtools::utils::PrintTimerDescription(__VA_ARGS__)
37
38
// Creates an object of ScopedTimer to measure the resource utilization for the
39
// scope surrounding it as the following example:
40
//
41
//   {   // <-- beginning of this scope
42
//
43
//     /* ... code out of interest ... */
44
//
45
//     SPIRV_TIMER_SCOPED(std::cout, tag);
46
//
47
//     /* ... lines of code that we want to know its resource usage ... */
48
//
49
//   }   // <-- end of this scope. The destructor of ScopedTimer prints tag and
50
//              the resource utilization to std::cout.
51
#define SPIRV_TIMER_SCOPED(...)                                         \
52
89.1k
  spvtools::utils::ScopedTimer<spvtools::utils::Timer> timer##__LINE__( \
53
178k
      __VA_ARGS__)
54
55
namespace spvtools {
56
namespace utils {
57
58
// Prints the description of resource types measured by Timer class. If |out| is
59
// NULL, it does nothing. Otherwise, it prints resource types. The second is
60
// optional and if it is true, the function also prints resource type fields
61
// related to memory. Its default is false. In usual, this must be placed before
62
// calling Timer::Report() to inform what those fields printed by
63
// Timer::Report() indicate.
64
void PrintTimerDescription(std::ostream*, bool = false);
65
66
// Status of Timer. kGetrusageFailed means it failed in calling getrusage().
67
// kClockGettimeWalltimeFailed means it failed in getting wall time when calling
68
// clock_gettime(). kClockGettimeCPUtimeFailed means it failed in getting CPU
69
// time when calling clock_gettime().
70
enum UsageStatus {
71
  kSucceeded = 0,
72
  kGetrusageFailed = 1 << 0,
73
  kClockGettimeWalltimeFailed = 1 << 1,
74
  kClockGettimeCPUtimeFailed = 1 << 2,
75
};
76
77
// Timer measures the resource utilization for a range of code. The resource
78
// utilization consists of CPU time (i.e., process time), WALL time (elapsed
79
// time), USR time, SYS time, RSS delta, and the delta of the number of page
80
// faults. RSS delta and the delta of the number of page faults are measured
81
// only when |measure_mem_usage| given to the constructor is true. This class
82
// should be used as the following example:
83
//
84
//   spvtools::utils::Timer timer(std::cout);
85
//   timer.Start();       // <-- set |usage_before_|, |wall_before_|,
86
//                               and |cpu_before_|
87
//
88
//   /* ... lines of code that we want to know its resource usage ... */
89
//
90
//   timer.Stop();        // <-- set |cpu_after_|, |wall_after_|, and
91
//                               |usage_after_|
92
//   timer.Report(tag);   // <-- print tag and the resource utilization to
93
//                               std::cout.
94
class Timer {
95
 public:
96
  Timer(std::ostream* out, bool measure_mem_usage = false)
97
89.1k
      : report_stream_(out),
98
89.1k
        usage_status_(kSucceeded),
99
89.1k
        measure_mem_usage_(measure_mem_usage) {}
100
101
  // Sets |usage_before_|, |wall_before_|, and |cpu_before_| as results of
102
  // getrusage(), clock_gettime() for the wall time, and clock_gettime() for the
103
  // CPU time respectively. Note that this method erases all previous state of
104
  // |usage_before_|, |wall_before_|, |cpu_before_|.
105
  virtual void Start();
106
107
  // Sets |cpu_after_|, |wall_after_|, and |usage_after_| as results of
108
  // clock_gettime() for the wall time, and clock_gettime() for the CPU time,
109
  // getrusage() respectively. Note that this method erases all previous state
110
  // of |cpu_after_|, |wall_after_|, |usage_after_|.
111
  virtual void Stop();
112
113
  // If |report_stream_| is NULL, it does nothing. Otherwise, it prints the
114
  // resource utilization (i.e., CPU/WALL/USR/SYS time, RSS delta) between the
115
  // time of calling Timer::Start() and the time of calling Timer::Stop(). If we
116
  // cannot get a resource usage because of failures, it prints "Failed" instead
117
  // for the resource.
118
  void Report(const char* tag);
119
120
  // Returns the measured CPU Time (i.e., process time) for a range of code
121
  // execution. If kClockGettimeCPUtimeFailed is set by the failure of calling
122
  // clock_gettime(), it returns -1.
123
0
  virtual double CPUTime() {
124
0
    if (usage_status_ & kClockGettimeCPUtimeFailed) return -1;
125
0
    return TimeDifference(cpu_before_, cpu_after_);
126
0
  }
127
128
  // Returns the measured Wall Time (i.e., elapsed time) for a range of code
129
  // execution. If kClockGettimeWalltimeFailed is set by the failure of
130
  // calling clock_gettime(), it returns -1.
131
0
  virtual double WallTime() {
132
0
    if (usage_status_ & kClockGettimeWalltimeFailed) return -1;
133
0
    return TimeDifference(wall_before_, wall_after_);
134
0
  }
135
136
  // Returns the measured USR Time for a range of code execution. If
137
  // kGetrusageFailed is set because of the failure of calling getrusage(), it
138
  // returns -1.
139
0
  virtual double UserTime() {
140
0
    if (usage_status_ & kGetrusageFailed) return -1;
141
0
    return TimeDifference(usage_before_.ru_utime, usage_after_.ru_utime);
142
0
  }
143
144
  // Returns the measured SYS Time for a range of code execution. If
145
  // kGetrusageFailed is set because of the failure of calling getrusage(), it
146
  // returns -1.
147
0
  virtual double SystemTime() {
148
0
    if (usage_status_ & kGetrusageFailed) return -1;
149
0
    return TimeDifference(usage_before_.ru_stime, usage_after_.ru_stime);
150
0
  }
151
152
  // Returns the measured RSS delta for a range of code execution. If
153
  // kGetrusageFailed is set because of the failure of calling getrusage(), it
154
  // returns -1.
155
0
  virtual long RSS() const {
156
0
    if (usage_status_ & kGetrusageFailed) return -1;
157
0
    return usage_after_.ru_maxrss - usage_before_.ru_maxrss;
158
0
  }
159
160
  // Returns the measured the delta of the number of page faults for a range of
161
  // code execution. If kGetrusageFailed is set because of the failure of
162
  // calling getrusage(), it returns -1.
163
0
  virtual long PageFault() const {
164
0
    if (usage_status_ & kGetrusageFailed) return -1;
165
0
    return (usage_after_.ru_minflt - usage_before_.ru_minflt) +
166
0
           (usage_after_.ru_majflt - usage_before_.ru_majflt);
167
0
  }
168
169
89.1k
  virtual ~Timer() {}
170
171
 private:
172
  // Returns the time gap between |from| and |to| in seconds.
173
0
  static double TimeDifference(const timeval& from, const timeval& to) {
174
0
    assert((to.tv_sec > from.tv_sec) ||
175
0
           (to.tv_sec == from.tv_sec && to.tv_usec >= from.tv_usec));
176
0
    return static_cast<double>(to.tv_sec - from.tv_sec) +
177
0
           static_cast<double>(to.tv_usec - from.tv_usec) * .000001;
178
0
  }
179
180
  // Returns the time gap between |from| and |to| in seconds.
181
0
  static double TimeDifference(const timespec& from, const timespec& to) {
182
0
    assert((to.tv_sec > from.tv_sec) ||
183
0
           (to.tv_sec == from.tv_sec && to.tv_nsec >= from.tv_nsec));
184
0
    return static_cast<double>(to.tv_sec - from.tv_sec) +
185
0
           static_cast<double>(to.tv_nsec - from.tv_nsec) * .000000001;
186
0
  }
187
188
  // Output stream to print out the resource utilization. If it is NULL,
189
  // Report() does nothing.
190
  std::ostream* report_stream_;
191
192
  // Status to stop measurement if a system call returns an error.
193
  unsigned usage_status_;
194
195
  // Variable to save the result of clock_gettime(CLOCK_PROCESS_CPUTIME_ID) when
196
  // Timer::Start() is called. It is used as the base status of CPU time.
197
  timespec cpu_before_;
198
199
  // Variable to save the result of clock_gettime(CLOCK_MONOTONIC) when
200
  // Timer::Start() is called. It is used as the base status of WALL time.
201
  timespec wall_before_;
202
203
  // Variable to save the result of getrusage() when Timer::Start() is called.
204
  // It is used as the base status of USR time, SYS time, and RSS.
205
  rusage usage_before_;
206
207
  // Variable to save the result of clock_gettime(CLOCK_PROCESS_CPUTIME_ID) when
208
  // Timer::Stop() is called. It is used as the last status of CPU time. The
209
  // resource usage is measured by subtracting |cpu_before_| from it.
210
  timespec cpu_after_;
211
212
  // Variable to save the result of clock_gettime(CLOCK_MONOTONIC) when
213
  // Timer::Stop() is called. It is used as the last status of WALL time. The
214
  // resource usage is measured by subtracting |wall_before_| from it.
215
  timespec wall_after_;
216
217
  // Variable to save the result of getrusage() when Timer::Stop() is called. It
218
  // is used as the last status of USR time, SYS time, and RSS. Those resource
219
  // usages are measured by subtracting |usage_before_| from it.
220
  rusage usage_after_;
221
222
  // If true, Timer reports the memory usage information too. Otherwise, Timer
223
  // reports only USR time, WALL time, SYS time.
224
  bool measure_mem_usage_;
225
};
226
227
// The purpose of ScopedTimer is to measure the resource utilization for a
228
// scope. Simply creating a local variable of ScopedTimer will call
229
// Timer::Start() and it calls Timer::Stop() and Timer::Report() at the end of
230
// the scope by its destructor. When we use this class, we must choose the
231
// proper Timer class (for class TimerType template) in advance. This class
232
// should be used as the following example:
233
//
234
//   {   // <-- beginning of this scope
235
//
236
//     /* ... code out of interest ... */
237
//
238
//     spvtools::utils::ScopedTimer<spvtools::utils::Timer>
239
//     scopedtimer(std::cout, tag);
240
//
241
//     /* ... lines of code that we want to know its resource usage ... */
242
//
243
//   }   // <-- end of this scope. The destructor of ScopedTimer prints tag and
244
//              the resource utilization to std::cout.
245
//
246
// The template<class TimerType> is used to choose a Timer class. Currently,
247
// only options for the Timer class are Timer and MockTimer in the unit test.
248
template <class TimerType>
249
class ScopedTimer {
250
 public:
251
  ScopedTimer(std::ostream* out, const char* tag,
252
              bool measure_mem_usage = false)
253
89.1k
      : timer(new TimerType(out, measure_mem_usage)), tag_(tag) {
254
89.1k
    timer->Start();
255
89.1k
  }
256
257
  // At the end of the scope surrounding the instance of this class, this
258
  // destructor saves the last status of resource usage and reports it.
259
89.1k
  virtual ~ScopedTimer() {
260
89.1k
    timer->Stop();
261
89.1k
    timer->Report(tag_);
262
89.1k
    delete timer;
263
89.1k
  }
264
265
 private:
266
  // Actual timer that measures the resource utilization. It must be an instance
267
  // of Timer class if there is no special reason to use other class.
268
  TimerType* timer;
269
270
  // A tag that will be printed in front of the trace reported by Timer class.
271
  const char* tag_;
272
};
273
274
// CumulativeTimer is the same as Timer class, but it supports a cumulative
275
// measurement as the following example:
276
//
277
//   CumulativeTimer *ctimer = new CumulativeTimer(std::cout);
278
//   ctimer->Start();
279
//
280
//   /* ... lines of code that we want to know its resource usage ... */
281
//
282
//   ctimer->Stop();
283
//
284
//   /* ... code out of interest ... */
285
//
286
//   ctimer->Start();
287
//
288
//   /* ... lines of code that we want to know its resource usage ... */
289
//
290
//   ctimer->Stop();
291
//   ctimer->Report(tag);
292
//   delete ctimer;
293
//
294
class CumulativeTimer : public Timer {
295
 public:
296
  CumulativeTimer(std::ostream* out, bool measure_mem_usage = false)
297
      : Timer(out, measure_mem_usage),
298
        cpu_time_(0),
299
        wall_time_(0),
300
        usr_time_(0),
301
        sys_time_(0),
302
        rss_(0),
303
0
        pgfaults_(0) {}
304
305
  // If we cannot get a resource usage because of failures, it sets -1 for the
306
  // resource usage.
307
0
  void Stop() override {
308
0
    Timer::Stop();
309
0
310
0
    if (cpu_time_ >= 0 && Timer::CPUTime() >= 0)
311
0
      cpu_time_ += Timer::CPUTime();
312
0
    else
313
0
      cpu_time_ = -1;
314
0
315
0
    if (wall_time_ >= 0 && Timer::WallTime() >= 0)
316
0
      wall_time_ += Timer::WallTime();
317
0
    else
318
0
      wall_time_ = -1;
319
0
320
0
    if (usr_time_ >= 0 && Timer::UserTime() >= 0)
321
0
      usr_time_ += Timer::UserTime();
322
0
    else
323
0
      usr_time_ = -1;
324
0
325
0
    if (sys_time_ >= 0 && Timer::SystemTime() >= 0)
326
0
      sys_time_ += Timer::SystemTime();
327
0
    else
328
0
      sys_time_ = -1;
329
0
330
0
    if (rss_ >= 0 && Timer::RSS() >= 0)
331
0
      rss_ += Timer::RSS();
332
0
    else
333
0
      rss_ = -1;
334
0
335
0
    if (pgfaults_ >= 0 && Timer::PageFault() >= 0)
336
0
      pgfaults_ += Timer::PageFault();
337
0
    else
338
0
      pgfaults_ = -1;
339
0
  }
340
341
  // Returns the cumulative CPU Time (i.e., process time) for a range of code
342
  // execution.
343
0
  double CPUTime() override { return cpu_time_; }
344
345
  // Returns the cumulative Wall Time (i.e., elapsed time) for a range of code
346
  // execution.
347
0
  double WallTime() override { return wall_time_; }
348
349
  // Returns the cumulative USR Time for a range of code execution.
350
0
  double UserTime() override { return usr_time_; }
351
352
  // Returns the cumulative SYS Time for a range of code execution.
353
0
  double SystemTime() override { return sys_time_; }
354
355
  // Returns the cumulative RSS delta for a range of code execution.
356
0
  long RSS() const override { return rss_; }
357
358
  // Returns the cumulative delta of number of page faults for a range of code
359
  // execution.
360
0
  long PageFault() const override { return pgfaults_; }
361
362
 private:
363
  // Variable to save the cumulative CPU time (i.e., process time).
364
  double cpu_time_;
365
366
  // Variable to save the cumulative wall time (i.e., elapsed time).
367
  double wall_time_;
368
369
  // Variable to save the cumulative user time.
370
  double usr_time_;
371
372
  // Variable to save the cumulative system time.
373
  double sys_time_;
374
375
  // Variable to save the cumulative RSS delta.
376
  long rss_;
377
378
  // Variable to save the cumulative delta of the number of page faults.
379
  long pgfaults_;
380
};
381
382
}  // namespace utils
383
}  // namespace spvtools
384
385
#else  // defined(SPIRV_TIMER_ENABLED)
386
387
#define SPIRV_TIMER_DESCRIPTION(...)
388
#define SPIRV_TIMER_SCOPED(...)
389
390
#endif  // defined(SPIRV_TIMER_ENABLED)
391
392
#endif  // SOURCE_UTIL_TIMER_H_