[timer] Boost Timer Library Version 2

Version 2 of Boost.Timer is about ready to move to trunk. The original version 1 timers are deprecated, but not changed in any way. Besides deciding to keep the version 2 timers in Boost.Timer, many suggestions and corrections from the "Boost.Timer replacement" thread have been applied. See http://lists.boost.org/Archives/boost/2011/09/185820.php The docs can be viewed at http://beman.github.com/timer/ The code is available at https://github.com/Beman/timer Thanks to all who posted comments, corrections, and suggestions in response to the original posting. Special thanks to Rob Stewart - many of his suggestions have been incorporated - and to Vicente Botet for his work on Boost.Chrono which now is used to implement much higher resolution for wall-clock time. While additional comments are always welcome, unless someone comes up with a showstopper I'm planning to move Version 2 to trunk and get on with other projects. --Beman

Beman Dawes wrote:
The docs can be viewed at http://beman.github.com/timer/
The Timer Home page should include an introduction and motivation before describing the two interfaces. CPU Timers page: - create_checkpoint() is confusing in the cpu_timer example because "checkpoint" appears so many different times, including in "last_checkpoint" and "checkpoint_timer". I initially thought that create_checkpoint() was a cpu_timer operation rather than the activity being controlled by the timer. I think it would be sufficient to change the introductory paragraph to, "The following code calls create_checkpoint() every 20 seconds of user CPU time:". (Note s/twenty/20/ and s/cpu/CPU/ in my version.) - s/by by/by/ - The Returns and Postconditions for various member functions are written in terms of expositional data members. You should add "as if" to those definitions or rephrase them to avoid mention of those data members. - I realize that the member functions are not very complicated and you have some future hope to see this code standardized, but the specification-style documentation is harder to follow than necessary for Boost documentation. For example, resume() should be documented as, "Starts the timer again, if currently stopped, as if it had been started when it was last stopped. That is, future calls to elapsed() will include the time it was stopped." The description for start() should note that the elapsed time is reset. - Why aren't you referring to default_format in the Effects clause for auto_cpu_timer::report()? - There's no information on thread safety. stop(), for example is not reentrant. - There's no information on Boost.Timers overhead relative to timing the target code.
The code is available at https://github.com/Beman/timer
I'm not sure the behavior of ~auto_cpu_timer() is correct. Shouldn't the times be reported if the timer was stopped but report() wasn't called? IOW, I'd have expected a "reported" flag that would control whether to try calling report(). I would not expect stop() to be a side effect of auto_cpu_timer::report(). I understand its usefulness for correct reporting, but I'd have expected this implementation: std::string const & fmt(m_format.empty() ? default_format : m_format); m_os << timer::format(stop(), fmt, m_places); resume(); I'd do s/cpu/CPU/ on default_format. (BTW, it's rather surprising that things at namespace scope declared in timer.hpp are not defined in timer.cpp.) auto_timers.cpp:94 assert(0) is unhelpful. The assertion should at least be: assert(!"Internal Error: Unexpected format character"); However, even better would be the following which accounts for "%%" producing "%" and "%x" producing "%x" for any "x" not in "wustp". The latter in the output would be a clue to the user that it was not a recognized conversion specification. if (*format != '%') { os << *format; } else if (*(format + 1)) { ++format; switch (*format) { case 'w': // and so on for u, s, t, and p break; case '%': os << '%'; break; default: os << '%' << *format; break; } } In cpu_timer.cpp::get_cpu_times(), on Windows, why do you throw away the perfectly good wall clock time when you can't get the process times? cpu_timer.cpp::tick_factor() is a non-trivial function, yet cpu_timer.cpp::get_cpu_times() calls it thrice in a row in the non-Windows code. Why is error_code.cpp included in Boost.Timer? Shouldn't you just use Boost.System? error_code.cpp:71: s/Sundo/Sun do/
Special thanks to Rob Stewart - many of his suggestions have been incorporated.
Thanks. Shouldn't some of those use cases be documented with examples to illustrate the flexibility of the API? _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com ________________________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

CPU Timers page: - There's no information on thread safety. stop(), for example is not reentrant. I guess that the functions are not thread-safe, as it is the case of Boost.Stopwatch. As far as the timers/stopwatches are stored on the stack there is no problem. The problem appears when the user shares
Le 27/09/11 17:42, Stewart, Robert a écrit : these timers/stopwatches between several threads.
- There's no information on Boost.Timers overhead relative to timing the target code.
Are you asking for the overhead of the time computations or the reporting? Best, Vicente

Vicente J. Botet Escriba wrote:
Le 27/09/11 17:42, Stewart, Robert a écrit :
CPU Timers page: - There's no information on thread safety.
I guess that the functions are not thread-safe, as it is the case of Boost.Stopwatch. As far as the timers/stopwatches are stored on the stack there is no problem. The problem appears when the user shares these timers/stopwatches between several threads.
Sure, I think that's probably right. I was just pointing out that there's no documentation regarding thread safety.
- There's no information on Boost.Timers overhead relative to timing the target code.
Are you asking for the overhead of the time computations or the reporting?
The former. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com ________________________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Le 28/09/11 13:12, Stewart, Robert a écrit :
Vicente J. Botet Escriba wrote:
Le 27/09/11 17:42, Stewart, Robert a écrit :
CPU Timers page: - There's no information on thread safety. I guess that the functions are not thread-safe, as it is the case of Boost.Stopwatch. As far as the timers/stopwatches are stored on the stack there is no problem. The problem appears when the user shares these timers/stopwatches between several threads. Sure, I think that's probably right. I was just pointing out that there's no documentation regarding thread safety. OK. I just wanted to be sure you were not requesting the library to ensure these functions were thread-safe.
- There's no information on Boost.Timers overhead relative to timing the target code.
Are you asking for the overhead of the time computations or the reporting? The former.
I will add some performance tests in Boost.Chrono to measure the time spent by the now functions and the difference between two time_points for each one of the clocks, so we can be fixed. Best, Vicente

On Tue, Sep 27, 2011 at 11:42 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
Beman Dawes wrote:
The docs can be viewed at http://beman.github.com/timer/
The Timer Home page should include an introduction and motivation before describing the two interfaces.
Done.
CPU Timers page:
- create_checkpoint() is confusing in the cpu_timer example because "checkpoint" appears so many different times, including in "last_checkpoint" and "checkpoint_timer". I initially thought that create_checkpoint() was a cpu_timer operation rather than the activity being controlled by the timer. I think it would be sufficient to change the introductory paragraph to, "The following code calls create_checkpoint() every 20 seconds of user CPU time:". (Note s/twenty/20/ and s/cpu/CPU/ in my version.)
Changed.
- s/by by/by/
Fixed.
- The Returns and Postconditions for various member functions are written in terms of expositional data members. You should add "as if" to those definitions or rephrase them to avoid mention of those data members.
Done.
- I realize that the member functions are not very complicated and you have some future hope to see this code standardized, but the specification-style documentation is harder to follow than necessary for Boost documentation. For example, resume() should be documented as, "Starts the timer again, if currently stopped, as if it had been started when it was last stopped. That is, future calls to elapsed() will include the time it was stopped." The description for start() should note that the elapsed time is reset.
I've done another pass through the reference section, making both corrections and clarifications. Where the resulting spec still seemed likely to baffle those not used to the style of the standard library, I've added a non-normative "Overview:" element.
- Why aren't you referring to default_format in the Effects clause for auto_cpu_timer::report()?
The default is supplied at construction. I've changed to a more robust implementation.
- There's no information on thread safety. stop(), for example is not reentrant.
OK, paragraph added.
- There's no information on Boost.Timers overhead relative to timing the target code.
I've added a "Timer accuracy" section to the docs. I'd welcome a patch to test/cpu_timer_info.cpp that actually measured overhead.
The code is available at https://github.com/Beman/timer
I'm not sure the behavior of ~auto_cpu_timer() is correct. Shouldn't the times be reported if the timer was stopped but report() wasn't called? IOW, I'd have expected a "reported" flag that would control whether to try calling report().
I need to give that a bit of thought...
I would not expect stop() to be a side effect of auto_cpu_timer::report(). I understand its usefulness for correct reporting, but I'd have expected this implementation:
std::string const & fmt(m_format.empty() ? default_format : m_format); m_os << timer::format(stop(), fmt, m_places); resume();
Good idea! Done.
I'd do s/cpu/CPU/ on default_format.
Done. (BTW, it's rather surprising that things at namespace scope declared in timer.hpp are not defined in timer.cpp.) That's been fixed.
auto_timers.cpp:94 assert(0) is unhelpful. The assertion should at least be:
assert(!"Internal Error: Unexpected format character");
However, even better would be the following which accounts for "%%" producing "%" and "%x" producing "%x" for any "x" not in "wustp". The latter in the output would be a clue to the user that it was not a recognized conversion specification.
if (*format != '%') { os << *format; } else if (*(format + 1)) { ++format; switch (*format) { case 'w': // and so on for u, s, t, and p break; case '%': os << '%'; break; default: os << '%' << *format; break; } }
The default and assert has been removed. KISS.
In cpu_timer.cpp::get_cpu_times(), on Windows, why do you throw away the perfectly good wall clock time when you can't get the process times?
Good catch! That was residue from the original implementation and should have been changed at the switch to use Boost Chrono. Fixed.
cpu_timer.cpp::tick_factor() is a non-trivial function, yet cpu_timer.cpp::get_cpu_times() calls it thrice in a row in the non-Windows code.
Actually, three times. Fixed.
Why is error_code.cpp included in Boost.Timer? Shouldn't you just use Boost.System?
Development artifact. Removed.
error_code.cpp:71: s/Sundo/Sun do/
Fixed in Boost trunk.
Special thanks to Rob Stewart - many of his suggestions have been incorporated.
Thanks. Shouldn't some of those use cases be documented with examples to illustrate the flexibility of the API?
Sure! Care to submit something? Actually, you've already contributed so much that I'd be happy to list you as a co-author! --Beman PS: docs updated at http://beman.github.com/timer/

Beman Dawes write:
On Tue, Sep 27, 2011 at 11:42 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
Beman Dawes wrote:
I've done another pass through the reference section, making both corrections and clarifications. Where the resulting spec still seemed likely to baffle those not used to the style of the standard library, I've added a non-normative "Overview:" element.
That's much better. I don't think that start() should be defined as storing anything. "Note" might be a better verb as it doesn't specify implementation. (This idea applies to stop(), too.)
- There's no information on thread safety. stop(), for example, is not reentrant.
OK, paragraph added.
Where?
- There's no information on Boost.Timers overhead relative to timing the target code.
I've added a "Timer accuracy" section to the docs. I'd welcome a patch to test/cpu_timer_info.cpp that actually measured overhead.
That's a nice start. In "Other concerns": - "such as when the clock is reset such as" is awkward. How about, "other processes. The clock may even run backward on some cases when, for example, the clock is reset due to a daylight saving time transition." (Note that the "S" in "DST" is "saving" not "savings".) - s/I.E/i.e.,/ - s/optimized way/optimized away/ In the second paragraph of "Recommendations", I'd change from "my personal preference" to "a good approach" or similar.
However, even better would be the following which accounts for "%%" producing "%" and "%x" producing "%x" for any "x" not in "wustp". The latter in the output would be a clue to the user that it was not a recognized conversion specification.
if (*format != '%') { os << *format; } else if (*(format + 1)) { ++format; switch (*format) { case 'w': // and so on for u, s, t, and p break; case '%': os << '%'; break; default: os << '%' << *format; break; } }
The default and assert has been removed. KISS.
I appreciate KISS, but I think my version is at least as simple as yours and it addresses what I think is surprising behavior, relative to printf(), with your current handling of %.
cpu_timer.cpp::tick_factor() is a non-trivial function, yet cpu_timer.cpp::get_cpu_times() calls it thrice in a row in ^^^^^^ the non-Windows code.
Actually, three times. Fixed.
See ^^^^^^ above ;-) I know, not many people use "thrice" these days.
Shouldn't some of those use cases be documented with examples to illustrate the flexibility of the API?
Sure! Care to submit something?
See my other e-mail regarding resume() for a start.
Actually, you've already contributed so much that I'd be happy to list you as a co-author!
I'd love that! Thank you. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com ________________________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Le 27/09/11 15:54, Beman Dawes a écrit :
Version 2 of Boost.Timer is about ready to move to trunk. The original version 1 timers are deprecated, but not changed in any way.
Besides deciding to keep the version 2 timers in Boost.Timer, many suggestions and corrections from the "Boost.Timer replacement" thread have been applied. See http://lists.boost.org/Archives/boost/2011/09/185820.php
The docs can be viewed at http://beman.github.com/timer/
The code is available at https://github.com/Beman/timer
Thanks to all who posted comments, corrections, and suggestions in response to the original posting.
Special thanks to Rob Stewart - many of his suggestions have been incorporated - and to Vicente Botet for his work on Boost.Chrono which now is used to implement much higher resolution for wall-clock time.
While additional comments are always welcome, unless someone comes up with a showstopper I'm planning to move Version 2 to trunk and get on with other projects.
Hi Beman, glad to see that you could make use of Boost.Chrono. I have some remark about the documentation. * The example using boost::timer::cpu_timer; ... nanosecond_type last_checkpoint = 0; cpu_timer checkpoint_timer; // start the timer while (more_transactions) { process_a_transaction(); if (checkpoint_timer.elapsed().user - last_checkpoint> 20*1000000000) { create_checkpoint(); last_checkpoint = checkpoint_timer.elapsed().user; } } seems to not really show the a well use of elapsed. For example the same behavior can be done with Boost.Chrono as follows using boost::chrono; ... process_user_cpu_clock::time_point last_checkpoint = process_user_cpu_clock::now(); while (more_transactions) { process_a_transaction(); if (process_user_cpu_clock::now() - last_checkpoint> seconds(20)) { create_checkpoint(); last_checkpoint = process_user_cpu_clock::now(); } } Note that the comparison is done using duration units. I guess that you should include a more specific example that show the advantage of using a timer. * By the way the user, wall and system fields are not chrono::duration? or why you don't use the chrono::process_cpu_clock that provides wall, user and system clocks at once? * Why does stop returns cpu-times? Why by reference? Why elapsed is not returned by reference? cpu_times elapsed() const noexcept; const cpu_times& stop() noexcept; * I would associate the operations pairwise start/stop and suspend/resume. I guess it would be easier to explain the semantics. Best, Vicente

Le 27/09/11 23:01, Vicente J. Botet Escriba a écrit :
Le 27/09/11 15:54, Beman Dawes a écrit :
While additional comments are always welcome, unless someone comes up with a showstopper I'm planning to move Version 2 to trunk and get on with other projects.
An additional remark, you are using the high_resolution_clock to measure the wall clock. high_resolution_clock is a system wide steady clock on platforms providing them, but system_clock (not steady) on the others. I guess that the user expects a steady clock while measuring the wall clock of the process. Unfortunately Boost.Chrono doesn't provides always a steady_clock class :( The documentation should warn the user of this possibility. Best, Vicente P.S. I need to add it also to the Boost.Chrono documentation of the process_cpu_clock class.

On Tue, Sep 27, 2011 at 6:24 PM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
Le 27/09/11 23:01, Vicente J. Botet Escriba a écrit :
Le 27/09/11 15:54, Beman Dawes a écrit :
While additional comments are always welcome, unless someone comes up with a showstopper I'm planning to move Version 2 to trunk and get on with other projects.
An additional remark,
you are using the high_resolution_clock to measure the wall clock. high_resolution_clock is a system wide steady clock on platforms providing them, but system_clock (not steady) on the others. I guess that the user expects a steady clock while measuring the wall clock of the process. Unfortunately Boost.Chrono doesn't provides always a steady_clock class :(
The documentation should warn the user of this possibility.
Good point! Will do. By the way, there was a late change to C++11 to add noexcept to the chrono clock's now() function. Can the Boost.Chrono now() function throw? The timer code assumes it doesn't. --Beman

Le 30/09/11 18:06, Beman Dawes a écrit :
By the way, there was a late change to C++11 to add noexcept to the chrono clock's now() function. Can the Boost.Chrono now() function throw? The timer code assumes it doesn't. I have added already noexcept in the trunk. Now not throws, but as we can have internal errors on the used library. In case of errors now() will assert. I don't know if this is the correct way to manage with this kind of functions.
If you want to manage the possible errors you could use now(ec); Best, Vicente

Vicente J. Botet Escriba wrote:
* Why does stop returns cpu-times? Why by reference? Why elapsed is not returned by reference?
cpu_times elapsed() const noexcept;
const cpu_times& stop() noexcept;
The interface is, of course, directed by the current implementation. Still, it would be appropriate for stop() to return by value to avoid forcing the implementation to store a cpu_times instance in the timer.
* I would associate the operations pairwise start/stop and suspend/resume. I guess it would be easier to explain the semantics.
Yes. Of course. I knew I didn't quite like the current semantics, but it just didn't click. Another advantage of start/stop and suspend/resume is that RAII classes can be used to control the timer. Here's the new interface: void start(); void stop(); bool is_stopped(); void suspend(); void resume(); bool is_suspended(); cpu_times elapsed(); start() notes the current time as the start time and marks the timer as not stopped, if is_stopped(), and marks the timer as not suspended. stop() notes the current time as the end time and marks the timer as stopped, if !is_stopped(), and marks the timer as not suspended. suspend() notes the current time as the suspension time and marks the object as being suspended, if !is_stopped() && !is_suspended(). resume() calls start() and arranges for a future call to elapsed() to include the time elapsed until suspend() was called plus the time that elapses subsequently, if is_suspended(). Reasonable? I know that means that both start and end times must be stored with this interface (two cpu_times instances, for example), but that seems a worthwhile price. Here's a use case addressed by these ideas: nanosecond_type status_time(0); cpu_timer timer; for (int i(0); i < 100; ++i) { task_to_time(); scoped_suspender _(timer); nanosecond_type const current_time(timer.elapsed().user); if (1000000000 < current_time - status_time) { report(timer); status_time = current_time; } } scoped_suspender is an RAII class that calls suspend() and resume(). Thus, the overhead of checking the elapsed time and writing output is not included in the time tracked by the timer. This approach permits doing non-timed work of various sorts in the midst of timed work. scoped_suspender might be a class template with a generator: auto _(make_scoped_suspender(timer)); That would allow supporting any timer type that provides suspend() and resume(). I suspect that this use case would be pretty common, so it could be captured: timer_reporter report(1000000000); // once per second cpu_timer timer; for (int i(0); i < 100; ++i) { task_to_time(); report(timer); } void timer_reporter::operator ()(cpu_timer & _timer) { if (_timer.is_stopped()) { return; } scoped_suspender _(_timer); nanosecond_type const current(_timer.elapsed().user); if (delay_ < current - last_) { std::string const formatted( _timer.format(precision_, format_)); os_ << formatted; last_ = current; } } Obviously, I'm suggesting that timer_reporter is somewhat similar to auto_cpu_timer in that it would save a std::ostream & for output, as well as the precision and a format string for calling cpu_timer::format(). I purposely did not supply the timer as a constructor argument because that would require construction after a timer already started or else stopping the timer, constructing the timer_reporter, and then starting the timer. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com ________________________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On Wed, Sep 28, 2011 at 8:38 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
Vicente J. Botet Escriba wrote:
* Why does stop returns cpu-times? Why by reference? Why elapsed is not returned by reference?
cpu_times elapsed() const noexcept;
const cpu_times& stop() noexcept;
The interface is, of course, directed by the current implementation. Still, it would be appropriate for stop() to return by value to avoid forcing the implementation to store a cpu_times instance in the timer.
There is only one set of times in the timer. How could an implementation not store the start times within the timer?
* I would associate the operations pairwise start/stop and suspend/resume. I guess it would be easier to explain the semantics.
Yes. Of course. I knew I didn't quite like the current semantics, but it just didn't click. Another advantage of start/stop and suspend/resume is that RAII classes can be used to control the timer.
Here's the new interface:
void start(); void stop(); bool is_stopped(); void suspend(); void resume(); bool is_suspended(); cpu_times elapsed();
I'm totally confused by having four states: stopped/suspended, stopped/not-suspended, not-stopped/suspended, not-stopped/not-suspended. What do the stopped/not-suppended and not-stopped/suspended states represent?
start() notes the current time as the start time and marks the timer as not stopped, if is_stopped(), and marks the timer as not suspended.
I can't parse that. What does ", if is_stopped()," apply to? Or does start() unconditionally mark the timer as not stopped and not suspended?
stop() notes the current time as the end time and marks the timer as stopped, if !is_stopped(), and marks the timer as not suspended.
Same problem with ", if is_stopped()," - what does the if apply to?
suspend() notes the current time as the suspension time and marks the object as being suspended, if !is_stopped() && !is_suspended().
Do you mean? Effects: If !is_stopped() && !is_suspended(), save the current time as the suspension time and mark the object as being suspended? Otherwise, no effects.
resume() calls start() and arranges for a future call to elapsed() to include the time elapsed until suspend() was called plus the time that elapses subsequently, if is_suspended().
Reasonable? I know that means that both start and end times must be stored with this interface (two cpu_times instances, for example), but that seems a worthwhile price.
For what? Are you looking for two timers that start at the same time: cpu_timer t1; cpu_timer t2(t1);
Here's a use case addressed by these ideas:
nanosecond_type status_time(0); cpu_timer timer; for (int i(0); i < 100; ++i) { task_to_time(); scoped_suspender _(timer); nanosecond_type const current_time(timer.elapsed().user); if (1000000000 < current_time - status_time) { report(timer); status_time = current_time; } }
scoped_suspender is an RAII class that calls suspend() and resume(). Thus, the overhead of checking the elapsed time and writing output is not included in the time tracked by the timer. This approach permits doing non-timed work of various sorts in the midst of timed work.
How is your example different from this? nanosecond_type status_time(0); cpu_timer timer; for (int i(0); i < 100; ++i) { task_to_time(); scoped_stop_and_resume(timer); // stop() in ctor, resume() in dtor nanosecond_type const current_time(timer.elapsed().user); if (1000000000 < current_time - status_time) { report(timer); status_time = current_time; } timer.resume(); }
scoped_suspender might be a class template with a generator:
auto _(make_scoped_suspender(timer));
That would allow supporting any timer type that provides suspend() and resume().
How is that different from stop() and resume() with current interface?
I suspect that this use case would be pretty common, so it could be captured:
timer_reporter report(1000000000); // once per second cpu_timer timer; for (int i(0); i < 100; ++i) { task_to_time(); report(timer); }
void timer_reporter::operator ()(cpu_timer & _timer) { if (_timer.is_stopped()) { return; } scoped_suspender _(_timer); nanosecond_type const current(_timer.elapsed().user); if (delay_ < current - last_) { std::string const formatted( _timer.format(precision_, format_)); os_ << formatted; last_ = current; } }
Obviously, I'm suggesting that timer_reporter is somewhat similar to auto_cpu_timer in that it would save a std::ostream & for output, as well as the precision and a format string for calling cpu_timer::format().
Understood.
I purposely did not supply the timer as a constructor argument because that would require construction after a timer already started or else stopping the timer, constructing the timer_reporter, and then starting the timer.
Interesting, --Beman

Beman Dawes wrote:
On Wed, Sep 28, 2011 at 8:38 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
Vicente J. Botet Escriba wrote:
* Why does stop returns cpu-times? Why by reference? Why elapsed is not returned by reference?
cpu_times elapsed() const noexcept;
const cpu_times& stop() noexcept;
The interface is, of course, directed by the current implementation. Still, it would be appropriate for stop() to return by value to avoid forcing the implementation to store a cpu_times instance in the timer.
There is only one set of times in the timer. How could an implementation not store the start times within the timer?
It needn't be stored as a cpu_times object, but see the end of this post.
* I would associate the operations pairwise start/stop and suspend/resume. I guess it would be easier to explain the semantics.
Here's the new interface:
void start(); void stop(); bool is_stopped(); void suspend(); void resume(); bool is_suspended(); cpu_times elapsed();
I'm totally confused by having four states: stopped/suspended, stopped/not-suspended, not-stopped/suspended, not-stopped/not-suspended.
stopped/suspended is an invalid state. suspend() only suspends if !is_stopped(). stopped/not suspended means stop() was called. The only valid transition is to call start(). Neither suspend() nor resume() does anything if is_stopped(). not stopped/suspended means suspend() was called. The timer is running, but suspended. There are two valid transitions: stop() or resume(). not stopped/not suspended means start() was called and neither stop() nor suspend() was called.
start() notes the current time as the start time and marks the timer as not stopped, if is_stopped(), and marks the timer as not suspended.
I can't parse that. What does ", if is_stopped()," apply to? Or does start() unconditionally mark the timer as not stopped and not suspended?
Try it this way: Effects: If is_stopped(), notes the current time as the start time. Postconditions: !is_stopped() and !is_suspended().
stop() notes the current time as the end time and marks the timer as stopped, if !is_stopped(), and marks the timer as not suspended.
Same problem with ", if is_stopped()," - what does the if apply to?
Effects: If !is_stopped(), notes the current time as the end time. Postconditions: is_stopped() and !is_suspended()
suspend() notes the current time as the suspension time and marks the object as being suspended, if !is_stopped() && !is_suspended().
Do you mean?
Effects: If !is_stopped() && !is_suspended(), save the current time as the suspension time and mark the object as being suspended? Otherwise, no effects.
Yes, though I'm purposely using "notes" rather than "saves" to avoid specifying the need to save anything to effect the behavior.
resume() calls start() and arranges for a future call to elapsed() to include the time elapsed until suspend() was called plus the time that elapses subsequently, if is_suspended().
Effects: As if start() is called and also arranging for a following call to elapsed() to include the time elapsed until the most recent call to suspend() plus the time that elapses subsequently, if is_suspended(). Postconditions: !is_stopped() and !is_suspended()
How is your example different from this?
nanosecond_type status_time(0); cpu_timer timer; for (int i(0); i < 100; ++i) { task_to_time(); scoped_stop_and_resume(timer); ^- some identifier nanosecond_type const current_time(timer.elapsed().user); if (1000000000 < current_time - status_time) { report(timer); status_time = current_time; } timer.resume(); ^^^^^^^^^^^^^^^ Not needed, given your scoped_stop_and_resume object.
}
How is that different from stop() and resume() with current interface?
Um, well, hmmm. It isn't. I guess I got the idea in my head that resume() provides lap counter behavior such that the final elapsed time would include the time spent calling report(timer), etc. start() and stop() have easy semantics as do suspend() and resume(). All four together are not, perhaps, quite as nice, given that resume() *doesn't* have lap counter behavior. It's still reasonable that stop() not return a value. Then start()/stop()/resume() is complete and consistent. I just have to quit thinking wrongly about resume()! "scoped_suspend" is nicer than "scoped_stop_and_resume", though. ;-) _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com ________________________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Le 28/09/11 21:21, Stewart, Robert a écrit :
Beman Dawes wrote:
On Wed, Sep 28, 2011 at 8:38 AM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
Vicente J. Botet Escriba wrote:
* Why does stop returns cpu-times? Why by reference? Why elapsed is not returned by reference?
cpu_times elapsed() const noexcept;
const cpu_times& stop() noexcept; The interface is, of course, directed by the current implementation. Still, it would be appropriate for stop() to return by value to avoid forcing the implementation to store a cpu_times instance in the timer. There is only one set of times in the timer. How could an implementation not store the start times within the timer? It needn't be stored as a cpu_times object, but see the end of this post. Boost.Stopwatch stores a time_point representing the last start or resume and a duration that cumulates the segments of a lap. A segment be defined by start->stop, start->suspend, resume->suspend and resume->stop.
* I would associate the operations pairwise start/stop and suspend/resume. I guess it would be easier to explain the semantics. Here's the new interface:
void start(); void stop(); bool is_stopped(); void suspend(); void resume(); bool is_suspended(); cpu_times elapsed(); I'm totally confused by having four states: stopped/suspended, stopped/not-suspended, not-stopped/suspended, not-stopped/not-suspended. stopped/suspended is an invalid state. suspend() only suspends if !is_stopped().
stopped/not suspended means stop() was called. The only valid transition is to call start(). Neither suspend() nor resume() does anything if is_stopped().
not stopped/suspended means suspend() was called. The timer is running, but suspended. There are two valid transitions: stop() or resume().
not stopped/not suspended means start() was called and neither stop() nor suspend() was called. I agree. The implementation can store whatever needs to represents these states. It is not forced to have two boolean fields stopped and suspended.
<snip>
How is your example different from this?
nanosecond_type status_time(0); cpu_timer timer; for (int i(0); i< 100; ++i) { task_to_time(); scoped_stop_and_resume(timer); ^- some identifier nanosecond_type const current_time(timer.elapsed().user); if (1000000000< current_time - status_time) { report(timer); status_time = current_time; } timer.resume(); ^^^^^^^^^^^^^^^ Not needed, given your scoped_stop_and_resume object.
}
How is that different from stop() and resume() with current interface? Um, well, hmmm. It isn't. I guess I got the idea in my head that resume() provides lap counter behavior such that the final elapsed time would include the time spent calling report(timer), etc.
start() and stop() have easy semantics as do suspend() and resume(). All four together are not, perhaps, quite as nice, given that resume() *doesn't* have lap counter behavior.
It's still reasonable that stop() not return a value. Then start()/stop()/resume() is complete and consistent. I just have to quit thinking wrongly about resume()!
"scoped_suspend" is nicer than "scoped_stop_and_resume", though. ;-)
Hi, in Boost.Stopwatch a lap is considered by the time between a start and a stop, possibly suspended/resumed several times. Laps can be stored in some kind of *memory*. The simple memory is a one-shot memory that gives you just the information about the current lap (or the last lap if the stopwatch is stopped. We can use also an accumulator set to memorize some statistics of several laps or just store the lap samples in a container for later processing. For this reason Boost.Stopwatch don't use stop and resume as you are proposing. I don't know if I have already said this in another post, Boost.Stopwatch provides scoped runners, stoppers, suspenders and resumers. The idea is that a scoped_operation realize the operation on the constructor and the pairwise on the destructor. Scoped runners are there to identify the context of a lap, scoped stoppers allow to identify the complementary of a lap. Best, Vicente

Vicente J. Botet Escriba wrote:
in Boost.Stopwatch a lap is considered by the time between a start and a stop, possibly suspended/resumed several times.
[snip]
For this reason Boost.Stopwatch don't use stop and resume as you are proposing.
Perhaps Boost.Timer could be designed now to be consistent with Boost.Stopwatch later, without including suspend() now or making breaking changes later. That would provide simplicity now and compatibility later if (when) Boost.Stopwatch is accepted. (I'm looking for middle ground, if possible. I haven't looked at the suspend/resume versus start/stop semantics in Stopwatch to see whether they are a good fit for Timer.) ___ Rob ________________________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.
participants (3)
-
Beman Dawes
-
Stewart, Robert
-
Vicente J. Botet Escriba