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 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 spvtools::utils::ScopedTimer<spvtools::utils::Timer> timer##__LINE__( \
53 __VA_ARGS__)
54
55namespace spvtools {
56namespace 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.
64void 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().
70enum 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.
94class Timer {
95 public:
96 Timer(std::ostream* out, bool measure_mem_usage = false)
97 : report_stream_(out),
98 usage_status_(kSucceeded),
99 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 virtual double CPUTime() {
124 if (usage_status_ & kClockGettimeCPUtimeFailed) return -1;
125 return TimeDifference(cpu_before_, cpu_after_);
126 }
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 virtual double WallTime() {
132 if (usage_status_ & kClockGettimeWalltimeFailed) return -1;
133 return TimeDifference(wall_before_, wall_after_);
134 }
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 virtual double UserTime() {
140 if (usage_status_ & kGetrusageFailed) return -1;
141 return TimeDifference(usage_before_.ru_utime, usage_after_.ru_utime);
142 }
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 virtual double SystemTime() {
148 if (usage_status_ & kGetrusageFailed) return -1;
149 return TimeDifference(usage_before_.ru_stime, usage_after_.ru_stime);
150 }
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 virtual long RSS() const {
156 if (usage_status_ & kGetrusageFailed) return -1;
157 return usage_after_.ru_maxrss - usage_before_.ru_maxrss;
158 }
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 virtual long PageFault() const {
164 if (usage_status_ & kGetrusageFailed) return -1;
165 return (usage_after_.ru_minflt - usage_before_.ru_minflt) +
166 (usage_after_.ru_majflt - usage_before_.ru_majflt);
167 }
168
169 virtual ~Timer() {}
170
171 private:
172 // Returns the time gap between |from| and |to| in seconds.
173 static double TimeDifference(const timeval& from, const timeval& to) {
174 assert((to.tv_sec > from.tv_sec) ||
175 (to.tv_sec == from.tv_sec && to.tv_usec >= from.tv_usec));
176 return static_cast<double>(to.tv_sec - from.tv_sec) +
177 static_cast<double>(to.tv_usec - from.tv_usec) * .000001;
178 }
179
180 // Returns the time gap between |from| and |to| in seconds.
181 static double TimeDifference(const timespec& from, const timespec& to) {
182 assert((to.tv_sec > from.tv_sec) ||
183 (to.tv_sec == from.tv_sec && to.tv_nsec >= from.tv_nsec));
184 return static_cast<double>(to.tv_sec - from.tv_sec) +
185 static_cast<double>(to.tv_nsec - from.tv_nsec) * .000000001;
186 }
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 // resouce 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 // resouce 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 resouce
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.
248template <class TimerType>
249class ScopedTimer {
250 public:
251 ScopedTimer(std::ostream* out, const char* tag,
252 bool measure_mem_usage = false)
253 : timer(new TimerType(out, measure_mem_usage)), tag_(tag) {
254 timer->Start();
255 }
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 virtual ~ScopedTimer() {
260 timer->Stop();
261 timer->Report(tag_);
262 delete timer;
263 }
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//
294class 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 pgfaults_(0) {}
304
305 // If we cannot get a resource usage because of failures, it sets -1 for the
306 // resource usage.
307 void Stop() override {
308 Timer::Stop();
309
310 if (cpu_time_ >= 0 && Timer::CPUTime() >= 0)
311 cpu_time_ += Timer::CPUTime();
312 else
313 cpu_time_ = -1;
314
315 if (wall_time_ >= 0 && Timer::WallTime() >= 0)
316 wall_time_ += Timer::WallTime();
317 else
318 wall_time_ = -1;
319
320 if (usr_time_ >= 0 && Timer::UserTime() >= 0)
321 usr_time_ += Timer::UserTime();
322 else
323 usr_time_ = -1;
324
325 if (sys_time_ >= 0 && Timer::SystemTime() >= 0)
326 sys_time_ += Timer::SystemTime();
327 else
328 sys_time_ = -1;
329
330 if (rss_ >= 0 && Timer::RSS() >= 0)
331 rss_ += Timer::RSS();
332 else
333 rss_ = -1;
334
335 if (pgfaults_ >= 0 && Timer::PageFault() >= 0)
336 pgfaults_ += Timer::PageFault();
337 else
338 pgfaults_ = -1;
339 }
340
341 // Returns the cumulative CPU Time (i.e., process time) for a range of code
342 // execution.
343 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 double WallTime() override { return wall_time_; }
348
349 // Returns the cumulative USR Time for a range of code execution.
350 double UserTime() override { return usr_time_; }
351
352 // Returns the cumulative SYS Time for a range of code execution.
353 double SystemTime() override { return sys_time_; }
354
355 // Returns the cumulative RSS delta for a range of code execution.
356 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 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_
393