LehrFEM++ 1.0.0
A simple Finite Element Library for teaching
timer.cc
1
9#include "timer.h"
10
11#include <fmt/format.h>
12
13#include <boost/config.hpp>
14#include <chrono>
15#include <iostream>
16
17#if defined(_WIN32) || defined(WIN32)
18#include <Windows.h>
19#else
20#include <sys/times.h>
21#include <unistd.h>
22#endif
23
24namespace /* anonymous */ {
25
26#if defined(_WIN32) || defined(WIN32)
27#else
28std::int_least64_t tick_factor() // multiplier to convert ticks
29 // to nanoseconds; -1 if unknown
30{
31 static std::int_least64_t tick_factor = 0;
32 if (tick_factor == 0) {
33 if ((tick_factor = ::sysconf(_SC_CLK_TCK)) <= 0) {
34 tick_factor = -1;
35 } else {
36 tick_factor = INT64_C(1000000000) / tick_factor; // compute factor
37 if (tick_factor == 0) {
38 tick_factor = -1;
39 }
40 }
41 }
42 return tick_factor;
43}
44#endif
45
46void get_cpu_times(lf::base::Timer::cpu_times* const current) {
47 std::chrono::nanoseconds x(
48 std::chrono::high_resolution_clock::now().time_since_epoch());
49 current->wall = std::chrono::nanoseconds(x.count());
50
51#if defined(_WIN32) || defined(WIN32)
52
53 FILETIME creation, exit;
54 if (::GetProcessTimes(::GetCurrentProcess(), &creation, &exit,
55 (LPFILETIME)&current->system,
56 (LPFILETIME)&current->user)) {
57 current->user *= 100; // Windows uses 100 nanosecond ticks
58 current->system *= 100;
59 } else
60
61 {
62 current->system = current->user = std::chrono::nanoseconds(-1);
63 }
64#else
65 tms tm; // NOLINT
66 clock_t c = ::times(&tm);
67 if (c == static_cast<clock_t>(-1)) // error
68 {
69 current->system = current->user = std::chrono::nanoseconds(-1);
70 } else {
71 current->system = std::chrono::nanoseconds(tm.tms_stime + tm.tms_cstime);
72 current->user = std::chrono::nanoseconds(tm.tms_utime + tm.tms_cutime);
73 int_least64_t factor;
74 if ((factor = tick_factor()) != -1) {
75 current->user *= factor;
76 current->system *= factor;
77 } else {
78 current->user = current->system = std::chrono::nanoseconds(-1);
79 }
80 }
81#endif
82}
83} // namespace
84
85namespace lf::base {
87 if (IsStopped()) {
88 return times_;
89 }
90
91 cpu_times current; // NOLINT
92 get_cpu_times(&current);
93 current.wall -= times_.wall;
94 current.user -= times_.user;
95 current.system -= times_.system;
96
97 return current;
98}
99
100std::string Timer::Format(std::string_view format) const {
101 const double sec = 1000000000.0L;
102 double wall_sec = static_cast<double>(times_.wall.count()) / sec;
103 auto total = times_.system.count() + times_.user.count();
104 double total_sec = static_cast<double>(total) / sec;
105 double percent = (total_sec / wall_sec) * 100.0;
106
107 return fmt::format(
108 format, fmt::arg("w", wall_sec),
109 fmt::arg("u", static_cast<double>(times_.user.count()) / sec),
110 fmt::arg("s", static_cast<double>(times_.system.count()) / sec),
111 fmt::arg("t", total_sec), fmt::arg("p", percent));
112}
113
114void Timer::Start() noexcept {
115 is_stopped_ = false;
116 get_cpu_times(&times_);
117}
118
119void Timer::Stop() noexcept {
120 if (IsStopped()) {
121 return;
122 }
123 is_stopped_ = true;
124
125 cpu_times current; // NOLINT
126 get_cpu_times(&current);
127 times_.wall = (current.wall - times_.wall);
128 times_.user = (current.user - times_.user);
129 times_.system = (current.system - times_.system);
130}
131
132void Timer::Resume() noexcept {
133 if (IsStopped()) {
134 cpu_times current(times_);
135 Start();
136 times_.wall -= current.wall;
137 times_.user -= current.user;
138 times_.system -= current.system;
139 }
140}
141
142AutoTimer::AutoTimer(std::string format)
143 : format_(std::move(format)), output_(&std::cout) {}
144
145AutoTimer::AutoTimer(std::ostream& stream, std::string format)
146 : format_(std::move(format)), output_(&stream) {}
147
148AutoTimer::AutoTimer(std::shared_ptr<spdlog::logger> logger,
149 spdlog::level::level_enum level, std::string format)
150 : format_(std::move(format)),
151 output_(std::make_pair(std::move(logger), level)) {}
152
154 if (!timer_.IsStopped()) {
155 timer_.Stop();
156 try {
157 Report();
158 } catch (...) { // eat any exceptions
159 }
160 }
161}
162
163std::ostream& AutoTimer::ostream() const {
164 return *std::get<std::ostream*>(output_);
165}
166
167const std::shared_ptr<spdlog::logger>& AutoTimer::logger() const {
168 return std::get<std::pair<std::shared_ptr<spdlog::logger>,
169 spdlog::level::level_enum>>(output_)
170 .first;
171}
172
173const std::string& AutoTimer::FormatString() const { return format_; }
174
176 auto string = timer_.Format(format_);
177 if (auto* ostream = std::get_if<std::ostream*>(&output_)) {
178 (**ostream) << string << std::endl;
179 } else {
180 std::get<1>(output_).first->log(std::get<1>(output_).second, string);
181 }
182}
183
185 return timer_.Elapsed();
186}
187
188std::string AutoTimer::Format(std::string_view format) const {
189 return timer_.Format(format);
190}
191
192} // namespace lf::base
std::variant< std::ostream *, std::pair< std::shared_ptr< spdlog::logger >, spdlog::level::level_enum > > output_
Definition: timer.h:272
std::string Format(std::string_view format=Timer::kDefaultFormat) const
Return the number of elapsed seconds of the wall clock time, user time and system time as a formatted...
Definition: timer.cc:188
Timer::cpu_times Elapsed() const noexcept
Retrieve the elapsed time since the construction.
Definition: timer.cc:184
AutoTimer(std::string format=Timer::kDefaultFormat)
Create a new AutoTimer that will report time measurements to std::cout when destructed.
Definition: timer.cc:142
const std::shared_ptr< spdlog::logger > & logger() const
Retrieve the logger that was passed in the constructor.
Definition: timer.cc:167
~AutoTimer()
Destructor of AutoTimer, calls Report().
Definition: timer.cc:153
const std::string & FormatString() const
Retrieve the format string that was passed in the constructor.
Definition: timer.cc:173
std::ostream & ostream() const
Retrieve the output stream object that was passed in the constructor.
Definition: timer.cc:163
void Report()
Report time (wall, user, system) since construction of AutoTimer() to either a std::ostream or spdlog...
Definition: timer.cc:175
std::string format_
Definition: timer.h:267
bool is_stopped_
Definition: timer.h:168
std::string Format(std::string_view format=kDefaultFormat) const
Return the number of elapsed seconds of the wall clock time, user time and system time as a formatted...
Definition: timer.cc:100
void Stop() noexcept
stop the timer, stop counting the elapsed time.
Definition: timer.cc:119
void Resume() noexcept
Resume the timer, i.e. start counting from where we stopped the last time.
Definition: timer.cc:132
cpu_times Elapsed() const noexcept
Elapsed time since timer start, doesn't stop the timer.
Definition: timer.cc:86
bool IsStopped() const noexcept
Is the timer currently stopped?
Definition: timer.h:114
cpu_times times_
Definition: timer.h:167
void Start() noexcept
(Re)starts the timer, i.e. the timer starts counting from 0 when this method is called.
Definition: timer.cc:114
Contains basic functionality that is used by other parts of LehrFEM++.
Definition: base.h:15
Packages the elapsed wall clock time, user process CPU time, and system process CPU time.
Definition: timer.h:69
std::chrono::nanoseconds system
Elapsed System time in nano-seconds.
Definition: timer.h:92
std::chrono::nanoseconds user
Elapsed User time in nano-seconds.
Definition: timer.h:84
std::chrono::nanoseconds wall
Elapsed WallClock time in nano-seconds.
Definition: timer.h:76