forked from LFGUI/LFGUI
-
Notifications
You must be signed in to change notification settings - Fork 1
/
stk_timer.h
324 lines (293 loc) · 10.3 KB
/
stk_timer.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
/*
* Copyright (C) 2012-2016 damu/gawag
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
#ifndef STK_TIMER_H
#define STK_TIMER_H
#include <iostream>
#include <vector>
#include <chrono>
/// \file
/// \brief Various timer classes to measure real time, used processor time and used cycles.
namespace stk
{
/// \brief The timer class can be used to measure times and optionaly output them automatically on destruction.
/// It supports multiple measurepoints and a sumarized output at the end.
///
/// Example:
/// \code
/// {
/// stk::timer t("test 1");
/// sleep(0.1);
/// cout<<"until now: "<<t.until_now()<<endl;
/// sleep(0.1);
/// } // the timer is destructed here as it goes out of scope and prints something like "0.100132 <- test"
/// {
/// stk::timer time("test 2");
/// sleep(0.1);
/// time.add("first");
/// sleep(0.2);
/// time.add("second");
/// sleep(0.3);
/// }
/// \endcode
/// Output:
/// \code
/// until now: 0.100005
/// 0.200011 <- test 1
/// 0.599034 <- test 2
/// 0.100005 <- 0 first
/// 0.200011 <- 1 second
/// 0.299017 to end
/// \endcode
class timer
{
class measure_point
{
public:
std::string text;
std::chrono::steady_clock::time_point start;
measure_point(std::string text) : text(text),start(std::chrono::steady_clock::now()) {}
};
std::string text;
std::vector<measure_point> points;
bool output; ///< if the timer should print something on destruction
public:
std::chrono::steady_clock::time_point start;
/// \brief The text is the general name for this timer. Setting output to false disables printing anything on destruction.
timer(std::string text="",bool output=true) : text(text),output(output) {start=std::chrono::steady_clock::now();}
/// \brief The text is the general name for this timer. Setting output to false disables printing anything on destruction.
timer(const char* text, bool output=true) : text(text),output(output) {start=std::chrono::steady_clock::now();}
timer(const timer&)=default;
timer(timer&&)=default;
timer& operator=(const timer&)=default;
timer& operator=(timer&&)=default;
~timer()
{
if(!output)
return;
auto start=this->start;
auto diff=std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now()-start).count()/1000000.0;
if(text.size())
std::cout<<diff<<" \t<- "<<text<<std::endl;
else
std::cout<<"timer time: "<<diff<<std::endl;
if(points.empty())
return;
for(auto p:points)
{
auto diff=std::chrono::duration_cast<std::chrono::microseconds>(p.start-start).count()/1000000.0;
std::cout<<" "<<diff<<" \t<- "<<p.text<<std::endl;
start=p.start;
}
diff=std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now()-start).count()/1000000.0;
std::cout<<" "<<diff<<" to end"<<std::endl;
}
/// \brief Returns the time passed since the timer was started or reset in seconds.
double until_now() const
{
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now()-start).count()/1000000.0;
}
void add(const std::string& name="")
{
if(!name.size())
points.emplace_back(std::to_string(points.size()));
else
points.emplace_back(std::to_string(points.size())+" "+name);
}
/// \brief Resets the starting time to the current time. Measure points added before this will display a negative time in the summary.
void reset(){start=std::chrono::steady_clock::now();}
};
/**
* @brief Same as timer but with the clock() function (measures only the time consumed by the program).
*/
class timer_clock
{
class measure_point
{
public:
std::string text;
clock_t tv;
measure_point(std::string text) : text(text),tv(::clock()) {}
};
std::string text;
std::vector<measure_point> points;
clock_t tv;
bool output; ///< if the timer should print something on destruction
public:
/// \brief The text is the general name for this timer. Setting output to false disables printing anything on destruction.
timer_clock(std::string text="",bool output=true) : text(text),tv(::clock()),output(output) {}
/// \brief The text is the general name for this timer. Setting output to false disables printing anything on destruction.
timer_clock(const char* text, bool output=true) : text(text),tv(::clock()),output(output) {}
timer_clock(const timer_clock&)=default;
timer_clock(timer_clock&&)=default;
timer_clock& operator=(const timer_clock&)=default;
timer_clock& operator=(timer_clock&&)=default;
~timer_clock()
{
if(!output)
return;
clock_t start=tv;
clock_t end=::clock();
if(text.size())
std::cout<<end-tv<<" \t<- "<<text<<std::endl;
else
std::cout<<"timer time: "<<end-tv<<std::endl;
if(points.empty())
return;
for(auto p:points)
{
std::cout<<" "<<p.tv-start<<" \t<- "<<p.text<<std::endl;
start=p.tv;
}
std::cout<<" "<<end-start<<" to end"<<std::endl;
}
/// \brief Returns the time passed since the timer was started or reset in seconds.
double until_now() const
{
clock_t end=::clock();
return end-tv;
}
void add(const std::string& name="")
{
if(!name.size())
points.emplace_back(std::to_string(points.size()));
else
points.emplace_back(std::to_string(points.size())+" "+name);
}
/// \brief Resets the starting time to the current time. Measure points added before this will display a negative time in the summary.
void reset(){tv=::clock();}
};
/// \brief This is a class to hierarchically profile code with manual set points in code. It uses the stk::timer class to measure times.
///
/// Example:
/// \code
/// {
/// stk::profiler::measure_point _("test1"); // points can be given names
/// sleep(0.1);
/// {
/// stk::profiler::measure_point _("test2"); // this point will be indent to show that it's a su-point of test1
/// sleep(0.1);
/// {
/// stk::profiler::measure_point _("test3");
/// sleep(0.1);
/// }
/// }
/// {
/// stk::profiler::measure_point _("test4");
/// sleep(0.1);
/// }
/// } // the test1 point measures the time until it's destruction here
/// {
/// STK_PROFILER // there is also a macro that will autogenerate names with the file
/// sleep(0.1); // name, line number and function name of its position, like ..\src\tests.cpp:107 main
/// {
/// STK_PROFILER
/// sleep(0.1);
/// {
/// STK_PROFILER
/// sleep(0.1);
/// }
/// }
/// {
/// STK_PROFILER
/// sleep(0.1);
/// }
/// }
/// \code
/// Output:
/// \code
/// 0.400022 test1
/// 0.200011 test2
/// 0.100005 test3
/// 0.100005 test4
/// 0.400022 ..\src\tests.cpp:107 main
/// 0.200011 ..\src\tests.cpp:110 main
/// 0.100005 ..\src\tests.cpp:113 main
/// 0.100005 ..\src\tests.cpp:118 main
/// \endcode
class profiler
{
public:
class measure_point
{
public:
std::string name;
stk::timer t=stk::timer("",false);
measure_point(const std::string& name) : name(name)
{
profiler::instance().open_points.push_back(this);
std::string str;
for(size_t i=0;i<profiler::instance().open_points.size()-1;i++)
str=str+" ";
str=str+profiler::instance().open_points.back()->name;
profiler::instance().find_or_create_time(str);
}
measure_point(const std::string& name,profiler*) : name(name)
{
}
~measure_point()
{
if(name=="STK Profiler") // crashing weirdly
return;
std::string str;
for(size_t i=0;i<profiler::instance().open_points.size()-1;i++)
str=str+" ";
str=str+profiler::instance().open_points.back()->name;
profiler::instance().find_or_create_time(str)+=t.until_now();
profiler::instance().open_points.resize(profiler::instance().open_points.size()-1);
}
};
private:
profiler() : main_point("STK Profiler",this) {}
measure_point main_point;
public:
std::vector<measure_point*> open_points;
std::vector<std::pair<std::string,double>> sub_point_times; // <name,time>
static profiler& instance()
{
static profiler p;
return p;
}
~profiler()
{
std::cout<<"Profiler: "<<std::endl;
for(auto e:sub_point_times)
std::cout<<e.second<<" \t"<<e.first<<std::endl;
}
double& find_or_create_time(const std::string& name)
{
for(std::pair<std::string,double>& e:sub_point_times)
if(e.first==name)
return e.second;
sub_point_times.emplace_back(name,0);
return sub_point_times.back().second;
}
static std::string to_string()
{
std::string ret;
ret.append("Profiler: \n");
for(auto e:instance().sub_point_times)
{
if(e.second<=0.0)
continue;
ret.append(std::to_string(e.second));
ret.append(" \t");
ret.append(e.first);
ret.append("\n");
}
return ret;
}
static void clear()
{
instance().sub_point_times.clear();
}
};
//#define STK_PROFILER stk::profiler_point STK_PROFILER_POINT(std::string(__FILE__)+":"+std::to_string(__LINE__)+" "+__PRETTY_FUNCTION__);
#define STK_PROFILER stk::profiler::measure_point __STK_PROFILER_POINT__(std::string(__FILE__)+":"+std::to_string(__LINE__)+" "+__FUNCTION__);
//#define STK_PROFILER stk::profiler_point STK_PROFILER_POINT(std::string(__FILE__)+":"+std::to_string(__LINE__));
#define STK_PROFILER_POINT(X) stk::profiler::measure_point __STK_PROFILER_POINT__(X);
} // namespace STK
#endif // STK_TIMER_H