Is there a function that converts nanoseconds to std :: string, say something like "3m12s"?
Not. But I will show you how you can easily do this below.
Should I use std :: chrono :: stable_clock :: now () every time I update my progress bar and subtract from _begin to determine time_left?
Yes.
Is there a better algorithm for determining time_left
Yes. See below.
Edit
I initially misunderstood โticksโ as โclock tactics,โ when in reality โticksโ have units of work, and _ticks_occurred/_total_ticks
can be interpreted as% job_done. So I changed the proposed progress_bar
below accordingly.
I believe the equation:
time_left = (time_taken / _total_ticks) * (_total_ticks - _ticks_occured)
wrong. It does not pass a health check: if _ticks_occured == 1
and _total_ticks
large, then time_left
approximately (ok, slightly less) time_taken
. It does not make sense.
I rewrite the above equation:
time_left = time_taken * (1/percent_done - 1)
Where
percent_done = _ticks_occurred/_total_ticks
Now, when percent_done
approaches zero, time_left
approaches infinity, and when percent_done
approaches 1, 'time_left
approaches 0. When percent_done
is 10%, time_left
is 9*time_taken
. This lives up to my expectations, assuming an approximately linear time value for each work mark.
class progress_bar { public: progress_bar(uint64_t ticks) : _total_ticks(ticks), _ticks_occurred(0), _begin(std::chrono::steady_clock::now()) // ... {} void tick() { using namespace std::chrono; // test to see if enough progress has elapsed // to warrant updating the progress bar // that way we aren't wasting resources printing // something that hasn't changed if (/* should we update */) { // somehow _ticks_occurred is updated here and is not zero duration time_taken = Clock::now() - _begin; float percent_done = (float)_ticks_occurred/_total_ticks; duration time_left = time_taken * static_cast<rep>(1/percent_done - 1); minutes minutes_left = duration_cast<minutes>(time_left); seconds seconds_left = duration_cast<seconds>(time_left - minutes_left); } } private: typedef std::chrono::steady_clock Clock; typedef Clock::time_point time_point; typedef Clock::duration duration; typedef Clock::rep rep; std::uint64_t _total_ticks; std::uint64_t _ticks_occurred; time_point _begin; //... };
Traffic in std :: chrono :: duration when you can. Thus, <chrono>
performs all the transformations for you. typedefs can facilitate typing with long names. And breaking the time in minutes and seconds is as simple as shown above.
As bames53 notes in his answer, if you want to use my <chrono_io>
object, this is also cool. Your needs may be simple enough that you do not want. This is a call to court. bames53's answer is a good answer. I thought these additional details might be helpful.
Edit
I accidentally left an error in the code above. And instead of just copying the code above, I thought it would be nice to point out the error and show how to use <chrono>
to fix it.
The error is here:
duration time_left = time_taken * static_cast<rep>(1/percent_done - 1);
and here:
typedef Clock::duration duration;
In practice, steady_clock::duration
usually based on an integral type. <chrono>
calls this rep
(abbreviation for presentation). And when percent_done
greater than 50%, the coefficient that is multiplied by time_taken
will be less than 1. And when rep
is integral, it turns out that it is 0. Thus, this progress_bar
behaves well during the first 50% and predicts 0 times in over the past 50%.
The key to fixing this is traffic in duration
, which is based on floating point instead of integers. And <chrono>
makes it very easy.
typedef std::chrono::steady_clock Clock; typedef Clock::time_point time_point; typedef Clock::period period; typedef std::chrono::duration<float, period> duration;
duration
now has the same tick period as steady_clock::duration
, but float
is used for presentation. And now the calculation for time_left
can leave static_cast:
duration time_left = time_taken * (1/percent_done - 1);
Here's the whole package with these fixes:
class progress_bar { public: progress_bar(uint64_t ticks) : _total_ticks(ticks), _ticks_occurred(0), _begin(std::chrono::steady_clock::now()) // ... {} void tick() { using namespace std::chrono; // test to see if enough progress has elapsed // to warrant updating the progress bar // that way we aren't wasting resources printing // something that hasn't changed if (/* should we update */) { // somehow _ticks_occurred is updated here and is not zero duration time_taken = Clock::now() - _begin; float percent_done = (float)_ticks_occurred/_total_ticks; duration time_left = time_taken * (1/percent_done - 1); minutes minutes_left = duration_cast<minutes>(time_left); seconds seconds_left = duration_cast<seconds>(time_left - minutes_left); std::cout << minutes_left.count() << "m " << seconds_left.count() << "s\n"; } } private: typedef std::chrono::steady_clock Clock; typedef Clock::time_point time_point; typedef Clock::period period; typedef std::chrono::duration<float, period> duration; std::uint64_t _total_ticks; std::uint64_t _ticks_occurred; time_point _begin; //... };
Nothing like a little testing ...; -)