Flow 1.0.0
Flow project: Full implementation reference.
checkpt_timer.cpp
Go to the documentation of this file.
1/* Flow
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
20#include <boost/algorithm/string.hpp>
21#include <time.h>
22#include <utility>
23
24namespace flow::perf
25{
26
27// Checkpointing_timer::Aggregator implementations.
28
30 std::string&& name_moved, Clock_types_subset which_clocks,
31 size_t max_n_checkpoints) :
32 log::Log_context(logger_ptr, Flow_log_component::S_PERF),
33 m_name(std::move(name_moved)),
34 m_which_clocks(which_clocks)
35 // m_start_when and m_last_checkpoint_when are currently garbage.
36{
38 using boost::algorithm::join;
39 using std::vector;
40 using std::string;
41
42 assert(max_n_checkpoints != 0);
43
44 m_checkpoints.reserve(max_n_checkpoints); // Performance -- avoid reallocs.
45
46 if (logger_ptr && logger_ptr->should_log(log::Sev::S_TRACE, get_log_component()))
47 {
48 vector<string> active_clock_type_strs;
49 active_clock_type_strs.reserve(m_which_clocks.size()); // Overboard but whatever....
50 for (size_t clock_type_idx = 0; clock_type_idx != m_which_clocks.size(); ++clock_type_idx)
51 {
52 if (m_which_clocks[clock_type_idx])
53 {
54 active_clock_type_strs.push_back(ostream_op_string(Clock_type(clock_type_idx)));
55 }
56 }
57
58 FLOW_LOG_TRACE_WITHOUT_CHECKING("Timer [" << m_name << "] created; "
59 "active clock types = [" << join(active_clock_type_strs, " | ") << "].");
60 }
61
62 // Start timing at the last moment: as close to the start of the user's measured op as possible.
64}
65
67 std::string&& name_moved, Clock_types_subset which_clocks) :
68 // (Reminder: This ctor is used for the Aggregator path (non-public).) Initialize everything const; nothing else.
69 log::Log_context(logger_ptr, Flow_log_component::S_PERF),
70 m_name(std::move(name_moved)),
71 m_which_clocks(which_clocks)
72 // m_start_when and m_last_checkpoint_when are currently garbage. m_checkpoints is not pre-reserved.
73{
74 // That's it.
75}
76
78{
79 Clock_types_subset clocks;
80 clocks.set(size_t(Clock_type::S_REAL_HI_RES));
81 return clocks;
82}
83
85{
86 Clock_types_subset clocks;
87 clocks.set(size_t(Clock_type::S_CPU_USER_LO_RES));
88 clocks.set(size_t(Clock_type::S_CPU_SYS_LO_RES));
89 clocks.set(size_t(Clock_type::S_CPU_TOTAL_HI_RES));
90 return clocks;
91}
92
94{
95 Clock_types_subset clocks;
96 clocks.set(size_t(Clock_type::S_CPU_THREAD_TOTAL_HI_RES));
97 return clocks;
98}
99
100void Checkpointing_timer::scale(uint64_t mult_scale, uint64_t div_scale)
101{
102 assert(m_checkpoints.size() != 0);
103
104 /* Please see doc header for our contract. Essentially we are to take a completed (1+ checkpoints) *this timer and
105 * change it to be as if it started at the recorded time, but each of the checkpoints completed not in recorded time T
106 * but time (T * mult_scale / div_scale). This involves updating the checkpoints themselves; and
107 * m_last_checkpoint_when which determines since_start(), the other key bit of user-interest info. */
108
109 /* - Always multiply first; overflow is not happening (64-bit); meanwhile it avoids rounding error to extent possible.
110 * - Reminder: all the stuff being modified aren't single values but arrays thereof (element per Clock_type). */
111
112 for (auto& checkpoint : m_checkpoints)
113 {
114 checkpoint.m_since_last *= mult_scale;
115 checkpoint.m_since_last /= div_scale;
116 }
117
118 /* Now let's "fix" since_start() accordingly. Of course since_start() is not an lvalue but equals
119 * (m_start_when + m_last_checkpoint_when). m_start_when is unchanged; so we are to fix m_last_checkpoint_when.
120 * An alternative way to think of it is simply that m_last_checkpoint_when == the time point(s) that resulted
121 * in m_checkpoints.back().m_since_last; and we had changed all the `m_since_last`s including the final one. */
122 auto scaled_since_start = since_start();
123 scaled_since_start *= mult_scale;
124 scaled_since_start /= div_scale;
126 m_last_checkpoint_when += scaled_since_start;
127 // (Could also get identical result by simply adding up all `checkpoint.m_since_last`s.)
128} // Checkpointing_timer::create_scaled()
129
131{
132 using ::timespec;
133 using ::clock_gettime;
134#if 0 // Not necessary anyway, but also in Linux this is a #define and hence not in a namespace.
135 using ::CLOCK_PROCESS_CPUTIME_ID;
136#endif
137 using boost::chrono::duration;
138 using boost::chrono::nanoseconds;
139
140 // Reminder: This call is extremely performance-centric. Keep it very low-fat.
141
142 switch (clock_type)
143 {
145 return Fine_clock::now();
147 return now_cpu_lo_res(now_cpu_lo_res_raw(), true);
149 return now_cpu_lo_res(now_cpu_lo_res_raw(), false);
151 {
152 /* @todo This is all fine, but ultimately the right thing to do would be to make a boost.chrono-ish clock
153 * that does the following in its now(). In fact there is a boost.chrono Trac ticket to do just that.
154 * Then <that clock>::now() would be called here. */
155 timespec time_spec;
156#ifndef NDEBUG
157 const bool ok = 0 ==
158#endif
159 clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time_spec);
160#ifndef NDEBUG
161 assert(ok);
162#endif
163 return Time_pt(nanoseconds(duration_rep_t(time_spec.tv_sec) * duration_rep_t(1000 * 1000 * 1000)
164 + duration_rep_t(time_spec.tv_nsec)));
165 }
167 {
168 // Similar to just above... keeping comments light.
169 timespec time_spec;
170#ifndef NDEBUG
171 const bool ok = 0 ==
172#endif
173 clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time_spec);
174#ifndef NDEBUG
175 assert(ok);
176#endif
177 return Time_pt(nanoseconds(duration_rep_t(time_spec.tv_sec) * duration_rep_t(1000 * 1000 * 1000)
178 + duration_rep_t(time_spec.tv_nsec)));
179 }
181 assert(false && "END_SENTINEL passed to now() -- must specify actual Clock_type.");
182 // Intentional: no `default:` -- to trigger typical compiler warning if an enum value is forgotten.
183 }
184
185 assert(false && "Bug? now() forgot to handle a Clock_type, yet compiler did not warn in switch()?");
186 return Time_pt();
187} // Checkpointing_timer::now(Clock_type)
188
190{
191 /* Reminder: This call is extremely performance-centric. Keep it very low-fat.
192 * Get all the clock values that were specified to get. Others skipped for performance. */
193
194 Time_pt_set time_pt_set; // Uninitialized garbage.
195 /* Would be unnecessary except that aggregation elsewhere doesn't worry about m_which_clocks.
196 * So just leave unused clock types' values at 0, and then aggregation will just add up a bunch of zeroes,
197 * when performance no longer matters. This fill() should be quite cheap.
198 * Also, string/stream output will list 0 on unused clocks instead of printing potential garbage. */
199 time_pt_set.m_values.fill(Time_pt());
200
201 const bool do_cpu_user_lo = which_clocks[size_t(Clock_type::S_CPU_USER_LO_RES)];
202 const bool do_cpu_sys_lo = which_clocks[size_t(Clock_type::S_CPU_SYS_LO_RES)];
203 if (do_cpu_user_lo || do_cpu_sys_lo)
204 {
205 const auto cpu_combo_now_raw = now_cpu_lo_res_raw();
206 if (do_cpu_user_lo)
207 {
208 time_pt_set.m_values[size_t(Clock_type::S_CPU_USER_LO_RES)] = now_cpu_lo_res(cpu_combo_now_raw, true);
209 if (do_cpu_sys_lo) // Doing this within the `if (){}` is a cheesy optimization... we're trying hard here.
210 {
211 time_pt_set.m_values[size_t(Clock_type::S_CPU_SYS_LO_RES)] = now_cpu_lo_res(cpu_combo_now_raw, false);
212 }
213 }
214 else // Continue the cheesy optimization started just above.
215 {
216 time_pt_set.m_values[size_t(Clock_type::S_CPU_SYS_LO_RES)] = now_cpu_lo_res(cpu_combo_now_raw, false);
217 }
218 }
219
220 if (which_clocks[size_t(Clock_type::S_CPU_TOTAL_HI_RES)])
221 {
222 time_pt_set.m_values[size_t(Clock_type::S_CPU_TOTAL_HI_RES)]
224 }
225
226 if (which_clocks[size_t(Clock_type::S_CPU_THREAD_TOTAL_HI_RES)])
227 {
230 }
231
232 if (which_clocks[size_t(Clock_type::S_REAL_HI_RES)])
233 {
234 time_pt_set.m_values[size_t(Clock_type::S_REAL_HI_RES)]
236 }
237
238 return time_pt_set;
239} // Checkpointing_timer::now(Clock_types_subset)
240
242{
243 return now(m_which_clocks);
244}
245
247{
248 // This is a struct of raw integers, each of which is a tick count since some epoch ref pt.
249 return Cpu_split_clock::now().time_since_epoch().count();
250}
251
253 bool user_else_sys) // Static.
254{
255 using boost::chrono::nanoseconds;
256
257 /* "Rebuild" the duration type of each *individual* component of the split-by-time-type struct stored by the
258 * Cpu_split_clock's unusual duration type. So the latter is like duration<::times, N>, where N is likely
259 * nano but could be something less precise; and the following is therefore like duration<uint64, N>.
260 * The key is that N needs to be the same; otherwise the below construction of a final Duration will use
261 * the wrong unit. (This is inside-baseball of this somewhat obscure boost.chrono class Cpu_split_clock.) */
262 using Cpu_split_component_duration = boost::chrono::duration<nanoseconds::rep, Cpu_split_clock::period>;
263
264 /* cpu_combo_now_raw is a struct of raw integers, each of which is a tick count for Cpu_split_component_duration,
265 * hence we can construct Cpu_split_component_duration from each. Probably type of each of the following is
266 * nanoseconds; but anything with that resolution or worse will convert OK. Otherwise it wouldn't compile below
267 * when converting to Duration in the construction of Time_pt. */
268 return Time_pt(Duration(Cpu_split_component_duration(user_else_sys ? cpu_combo_now_raw.user
269 : cpu_combo_now_raw.system)));
270} // Checkpointing_timer::now_cpu_lo_res()
271
273{
274 // Reminder: This call is extremely performance-centric. Keep it very low-fat. Obviously that includes now() call:
275
276 // Get the time ASAP, being as close to the potential end of measured user operation as possible.
277 const auto checkpoint_when = now();
278
279 // We don't want to allocate after initial reserve(). Ctor doc header (as of this writing) says undefined behavior.
280 assert((m_checkpoints.size() + 1) <= m_checkpoints.capacity());
281 // Save the new checkpoint.
282 m_checkpoints.push_back({ std::move(name_moved),
283 checkpoint_when - m_last_checkpoint_when });
284 // Update invariant.
285 m_last_checkpoint_when = checkpoint_when;
286
287 FLOW_LOG_TRACE("Timer [" << m_name << "]: Added checkpoint # [" << (m_checkpoints.size() - 1) << "]: "
288 "[" << m_checkpoints.back() << "]; total elapsed [" << since_start() << "].");
289
290 return m_checkpoints.back();
291}
292
294{
295 assert(!m_checkpoints.empty());
296 return m_last_checkpoint_when - m_start_when; // This is why m_last_checkpoint_when exists.
297}
298
299const std::vector<Checkpointing_timer::Checkpoint>& Checkpointing_timer::checkpoints() const
300{
301 return m_checkpoints;
302}
303
304void Checkpointing_timer::output(std::ostream* os) const
305{
306 // We promised to have multi-lines but no trailing newline. Basically assume we start on column 0.
307 assert(!m_checkpoints.empty());
308
309 (*os)
310 <<
311 "--- Timer [" << m_name << "] ----\n"
312 "Checkpoints (count = [" << m_checkpoints.size() << "]):\n";
313
314 size_t idx = 0;
315 for (const auto& checkpoint : m_checkpoints)
316 {
317 (*os) << " [#" << idx << "| " << checkpoint << ']';
318 if ((idx + 1) != m_checkpoints.size())
319 {
320 (*os) << '\n'; // Else no newline as promised.
321 }
322 ++idx;
323 }
324
325 if (m_checkpoints.size() == 1)
326 {
327 return; // No point, as total = the one checkpoint (a fairly common use case).
328 }
329 // else
330
331 (*os) << "\n"
332 "Total duration: [" << since_start() << "]"; // No newline as promised.
333}
334
335std::ostream& operator<<(std::ostream& os, const Checkpointing_timer::Checkpoint& checkpoint)
336{
337 return os << checkpoint.m_name << "<-[" << checkpoint.m_since_last << ']';
338}
339
340std::ostream& operator<<(std::ostream& os, const Checkpointing_timer& timer)
341{
342 timer.output(&os);
343 return os;
344}
345
346// Checkpointing_timer::Aggregator implementations.
347
349 std::string&& name_moved, size_t max_n_samples) :
350 log::Log_context(logger_ptr, Flow_log_component::S_PERF),
351 m_name(std::move(name_moved))
352{
353 assert(max_n_samples != 0);
354 m_timers.reserve(max_n_samples); // Performance -- avoid reallocs.
355}
356
358{
359 assert((m_timers.size() + 1) <= m_timers.capacity()); // Similarly to Checkpointing_timer::checkpoint()....
360 m_timers.push_back(timer_ptr); // Watch out for concurrency!
361
362 FLOW_LOG_TRACE("Aggregator [" << m_name << "]: Added Timer # [" << (m_timers.size() - 1) << "] "
363 "named [" << timer_ptr->m_name << "].");
364}
365
367{
368 m_timers.clear(); // Again, watch out for concurrency.
369}
370
372 (log::Logger* alternate_logger_ptr_or_null, bool info_log_result,
373 uint64_t mean_scale_or_zero) const
374{
375 using std::string;
376 using std::copy;
377
378 assert(!m_timers.empty());
379 auto model_timer = m_timers.front();
380
381 // Construct the thing minimally and fill out the rest of the fields as the public ctor would, with special values.
382 Checkpointing_timer_ptr agg_timer(new Checkpointing_timer(get_logger(), string(m_name), model_timer->m_which_clocks));
383 agg_timer->m_checkpoints.reserve(model_timer->m_checkpoints.size());
384 // m_start_when, m_last_checkpoint_when, and m_checkpoints itself are empty; we set them below.
385
386 bool first_dur = true;
387 Duration_set total_dur; // Uninitialized!
388
389 // Add it all up, making sure each timer's sequence of checkpoints is the same (names, etc.).
390 for (const auto& timer : m_timers)
391 {
392 if (timer != model_timer) // Sanity-check as noted -- first on the timer at large, below on checkpoints.
393 {
394 assert(timer->m_which_clocks == model_timer->m_which_clocks);
395 assert(timer->m_checkpoints.size() == model_timer->m_checkpoints.size());
396 }
397
398 size_t idx_checkpoints = 0; // So we can walk along model_timer->m_checkpoints in parallel.
399 for (const auto& checkpoint : timer->m_checkpoints)
400 {
401 if (timer == model_timer)
402 {
403 // First timer being examined. Start up this *result* checkpt to equal it. Later, we will += to it.
404 agg_timer->m_checkpoints.push_back(checkpoint);
405 }
406 else // if (timer != model_timer)
407 {
408 // Sanity-check the checkpoints line up as noted.
409#ifndef NDEBUG
410 const auto& model_checkpoint = model_timer->m_checkpoints[idx_checkpoints];
411 assert(checkpoint.m_name == model_checkpoint.m_name);
412#endif
413 // Name is all set (and is consistent); aggregate in the durations from the agged timer's checkpoint.
414 agg_timer->m_checkpoints[idx_checkpoints].m_since_last += checkpoint.m_since_last;
415 }
416
417 // Add to or start the uber-sum.
418 if (first_dur)
419 {
420 first_dur = false;
421 total_dur = checkpoint.m_since_last; // Uninitialized right now => assign.
422 }
423 else
424 {
425 // Initialized: sum.
426 total_dur += checkpoint.m_since_last;
427 }
428
429 ++idx_checkpoints;
430 }
431 } // for (timer : m_timers)
432
433 /* Note it's currently an uninitialized array; set it to 0s. Then the below calls will yield appropriate
434 * agg_timer->since_start() result. This is a bit of a hack, perhaps, but since_start() is not an lvalue, and this
435 * does work given that fact. */
436 agg_timer->m_start_when.m_values.fill(Time_pt());
437 agg_timer->m_last_checkpoint_when = agg_timer->m_start_when;
438 agg_timer->m_last_checkpoint_when += total_dur;
439 // This is what we really wanted, but since_start() isn't an lvalue hence the above hackery.
440 assert(agg_timer->since_start().m_values == total_dur.m_values);
441
442 const auto n_samples = m_timers.size();
443 const bool mean_aggregation = mean_scale_or_zero != 0;
444
445 if (mean_aggregation)
446 {
447 FLOW_LOG_TRACE("Returning aggregated-mean result timer [" << m_name << "] "
448 "with [" << n_samples << "] samples "
449 "scaled by [" << mean_scale_or_zero << "] samples.");
450 agg_timer->scale(mean_scale_or_zero, n_samples);
451 }
452 else
453 {
454 FLOW_LOG_TRACE("Returning aggregated-sum result timer [" << m_name << "] "
455 "with [" << n_samples << "] samples.");
456 }
457
458 /* This is typically the "official" way to log something user-friendly.
459 * (Sometimes they may have prepared their own agg_timer; in which case they can call
460 * log_aggregated_result_in_timer() [which is static] directly. See Checkpointing_timer doc header. */
461 if (info_log_result)
462 {
463 log_aggregated_result_in_timer(alternate_logger_ptr_or_null ? alternate_logger_ptr_or_null : get_logger(),
464 *agg_timer, n_samples, mean_scale_or_zero);
465 }
466
467 return agg_timer;
468} // Checkpointing_timer::Aggregator::create_aggregated_result()
469
471 (log::Logger* logger_ptr,
472 const Checkpointing_timer& agg_timer, unsigned int n_samples,
473 uint64_t mean_scale_or_zero) // Static.
474{
475 using std::array;
476 using flow::log::Logger;
477
478 FLOW_LOG_SET_CONTEXT(logger_ptr, Flow_log_component::S_PERF);
479 const bool mean_aggregation = mean_scale_or_zero != 0;
480
481 // Show one type (numerically speaking) of view into the aggregated results.
482 if (!mean_aggregation)
483 {
484 FLOW_LOG_INFO("Aggregator [" << agg_timer.m_name << "]: "
485 "Collated timing result from [" << n_samples << "] samples: "
486 "SUM: [\n" << agg_timer << "\n].");
487 }
488 else // if (mean_aggregation)
489 {
490 if (mean_scale_or_zero == 1)
491 {
492 FLOW_LOG_INFO("Aggregator [" << agg_timer.m_name << "]: "
493 "Collated timing result from [" << n_samples << "] samples: "
494 "MEAN: [\n" << agg_timer << "\n].");
495 }
496 else // if (mean_scale_or_zero != 1)
497 {
498 assert(mean_scale_or_zero != 1);
499 FLOW_LOG_INFO("Aggregator [" << agg_timer.m_name << "]: "
500 "Collated timing result from [" << n_samples << "] samples: "
501 "SCALED-MEAN per [" << mean_scale_or_zero << "] samples: [\n" << agg_timer << "\n].");
502 }
503 }
504} // Checkpointing_timer::Aggregator::log_aggregated_result_in_timer()
505
507 bool show_sum, bool show_mean,
508 uint64_t mean_scale_or_zero) const
509{
510 FLOW_LOG_INFO("Aggregation completed earlier. Collating aggregated timing data; logging here/elsewhere:");
511
512 // Show each type (numerically speaking) of view into the aggregated results.
513
514 if ((mean_scale_or_zero == 1) && show_mean)
515 {
516 /* If we're going to show MEAN anyway, and SCALED-MEAN's specified scale is the denegerate 1, then
517 * pretend they didn't enabled SCALED-MEAN output in the first place. It would show the same numbers anyway --
518 * twice; so just do it once. */
519 mean_scale_or_zero = 0;
520 }
521
522 // First `true` triggers actual logging; beyond that we ignore returned timer.
523 if (show_sum)
524 {
525 create_aggregated_result(alternate_logger_ptr_or_null, true, 0); // Log sum.
526 }
527 if (show_mean)
528 {
529 create_aggregated_result(alternate_logger_ptr_or_null, true, 1); // Ditto for simple mean.
530 }
531 if (mean_scale_or_zero != 0)
532 {
533 create_aggregated_result(alternate_logger_ptr_or_null, true, mean_scale_or_zero); // Ditto for scaled mean.
534 }
535} // Checkpointing_timer::Aggregator::log_aggregated_results()
536
537} // namespace flow::perf
const Component & get_log_component() const
Returns reference to the stored Component object, particularly as many FLOW_LOG_*() macros expect.
Definition: log.cpp:229
Logger * get_logger() const
Returns the stored Logger pointer, particularly as many FLOW_LOG_*() macros expect.
Definition: log.cpp:224
Interface that the user should implement, passing the implementing Logger into logging classes (Flow'...
Definition: log.hpp:1291
virtual bool should_log(Sev sev, const Component &component) const =0
Given attributes of a hypothetical message that would be logged, return true if that message should b...
void aggregate(Checkpointing_timer_ptr timer_ptr)
Register given Checkpointing_timer for aggregated results.
void log_aggregated_results(log::Logger *alternate_logger_ptr_or_null, bool show_sum, bool show_mean, uint64_t mean_scale_or_zero) const
Called after the final Checkpointing_timer has finished (final checkpoint() call) and been aggregate(...
static void log_aggregated_result_in_timer(log::Logger *logger_ptr, const Checkpointing_timer &agg_timer, unsigned int n_samples, uint64_t mean_scale_or_zero=1)
Given an aggregated-result Checkpointing_timer (as if returned by create_aggregated_result()) logs th...
Checkpointing_timer_ptr create_aggregated_result(log::Logger *alternate_logger_ptr_or_null, bool info_log_result, uint64_t mean_scale_or_zero=1) const
Provides programmatic access to the aggregation result that would be logged by log_aggregated_results...
Aggregator(log::Logger *logger_ptr, std::string &&name_moved, size_t max_n_samples)
Constructs a new, empty aggregator.
void clear()
Act as if aggregate() has never been called since construction.
std::vector< Checkpointing_timer_ptr > m_timers
The ref-counted pointers to source timers passed to aggregate() in that order.
The central class in the perf Flow module, this efficiently times the user's operation,...
void output(std::ostream *os) const
Equivalent to (*os) << (*this).
const std::vector< Checkpoint > & checkpoints() const
Returns the checkpoints recorded so far.
const std::string m_name
The immutable human-readable name for this timer. It's const so no need for accessor for now.
static Clock_types_subset process_cpu_clock_types()
Returns the bit-set containing only those Clock_types enabled that measure passage of processor time ...
void scale(uint64_t mult_scale, uint64_t div_scale)
Called anytime after the last checkpoint(), this modifies the data collected so far to be as if every...
Time_pt_set now() const
Sample all currently enabled Clock_types' clocks and return those values, each of which is a time sta...
Time_pt_set m_last_checkpoint_when
The value of now() recorded during the last checkpoint() call; or equals m_start_when if checkpoint()...
static Cpu_split_clock_durs_since_epoch now_cpu_lo_res_raw()
Returns the current Cpu_split_clock time, essentially as a combo struct containing raw (integer) coun...
static Time_pt now_cpu_lo_res(const Cpu_split_clock_durs_since_epoch &cpu_combo_now_raw, bool user_else_sys)
Returns individual Time_pt value returnable by now() various overloads: either the user time or syste...
static Clock_types_subset real_clock_types()
Returns the bit-set containing only those Clock_types enabled that measure passage of real (wall cloc...
std::vector< Checkpoint > m_checkpoints
The checkpoints recorded so far, in order, one per checkpoint() call.
const Checkpoint & checkpoint(std::string &&name_moved)
Records a checkpoint, which takes a sample of all enabled Clock_types' clocks and records the corresp...
Checkpointing_timer(log::Logger *logger_ptr, std::string &&name_moved, Clock_types_subset which_clocks, size_t max_n_checkpoints)
Constructs a new timer and immediately begins measuring time, in that the next checkpoint() call will...
static Clock_types_subset thread_cpu_clock_types()
Returns the bit-set containing only those Clock_types enabled that measure passage of processor time ...
Time_pt_set m_start_when
The value of now() saved in public constructor, marking the time point when the timing began.
const Clock_types_subset m_which_clocks
The bit-set indexed by size_t(Clock_type::...) wherein that clock will be sampled in now() and checkp...
Cpu_split_clock::times Cpu_split_clock_durs_since_epoch
Short-hand for the combo (user/kernel) durations-since-epoch tracked by Cpu_split_clock.
Duration_set since_start() const
Returns the duration(s) (for all enabled Clock_types) between construction and the last checkpoint() ...
#define FLOW_LOG_INFO(ARG_stream_fragment)
Logs an INFO message into flow::log::Logger *get_logger() with flow::log::Component get_log_component...
Definition: log.hpp:197
#define FLOW_LOG_TRACE_WITHOUT_CHECKING(ARG_stream_fragment)
Logs a TRACE message into flow::log::Logger *get_logger() with flow::log::Component get_log_component...
Definition: log.hpp:354
#define FLOW_LOG_SET_CONTEXT(ARG_logger_ptr, ARG_component_payload)
For the rest of the block within which this macro is instantiated, causes all FLOW_LOG_....
Definition: log.hpp:405
#define FLOW_LOG_TRACE(ARG_stream_fragment)
Logs a TRACE message into flow::log::Logger *get_logger() with flow::log::Component get_log_component...
Definition: log.hpp:227
@ S_TRACE
Message indicates any condition that may occur with great frequency (thus verbose if logged).
Flow module containing tools for profiling and optimization.
boost::shared_ptr< Checkpointing_timer > Checkpointing_timer_ptr
Short-hand for ref-counting pointer to Checkpointing_timer.
Definition: perf_fwd.hpp:43
Fine_duration Duration
Short-hand for a high-precision boost.chrono duration, formally equivalent to flow::Fine_duration.
std::ostream & operator<<(std::ostream &os, const Checkpointing_timer::Checkpoint &checkpoint)
Prints string representation of the given Checkpoint to the given ostream.
Duration::rep duration_rep_t
The raw type used in Duration to store its clock ticks.
std::bitset< size_t(Clock_type::S_END_SENTINEL)> Clock_types_subset
Short-hand for a bit-set of N bits which represents the presence or absence of each of the N possible...
Fine_time_pt Time_pt
Short-hand for a high-precision boost.chrono point in time, formally equivalent to flow::Fine_time_pt...
Clock_type
Clock types supported by flow::perf module facilities, perf::Checkpointing_timer in particular.
@ S_CPU_SYS_LO_RES
Counterpart of S_CPU_USER_LO_RES, measuring processor time (kernel-level) spent by the current proces...
@ S_END_SENTINEL
Final, invalid clock type; its numerical value equals the number of clocks currently supported.
@ S_REAL_HI_RES
Measures real time (not processor time), using the highest-resolution system clock available that gua...
@ S_CPU_THREAD_TOTAL_HI_RES
Similar to S_CPU_TOTAL_HI_RES but applied to the calling thread as opposed to entire process.
@ S_CPU_TOTAL_HI_RES
Measures processor time (user- and kernel-level total) spent by the current process; this is the high...
@ S_CPU_USER_LO_RES
Along with S_CPU_SYS_LO_RES, measures processor time (user-level) spent by the current process; this ...
std::string ostream_op_string(T const &... ostream_args)
Equivalent to ostream_op_to_string() but returns a new string by value instead of writing to the call...
Definition: util.hpp:356
Flow_log_component
The flow::log::Component payload enumeration comprising various log components used by Flow's own int...
Definition: common.hpp:632
The entirety of the information recorded with each Checkpointing_timer::checkpoint() call; it contain...
const std::string m_name
User-supplied name of the checkpoint.
Duration_set m_since_last
For the T.checkpoint() call that generated *this, for each enabled Clock_type: Time elapsed since eit...
Convenience wrapper around an array<Duration, N>, which stores a duration for each of the N possible ...
std::array< Duration, size_t(Clock_type::S_END_SENTINEL)> m_values
The underlying data, directly readable and writable by user. NOT ZERO-INITIALIZED ON CONSTRUCTION (or...
Convenience wrapper around an array<Time_pt, N>, which stores a time point for each of the N possible...
std::array< Time_pt, size_t(Clock_type::S_END_SENTINEL)> m_values
The underlying data, directly readable and writable by user. NOT ZERO-INITIALIZED ON CONSTRUCTION (or...