Estimated time remaining in C ++ 11

I am writing a runtime class class that displays an updated progress bar every n ticks on std::ostream :

 class progress_bar { public: progress_bar(uint64_t ticks) : _total_ticks(ticks), ticks_occured(0), _begin(std::chrono::steady_clock::now()) ... void tick() { // 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 */) { ... } } private: std::uint64_t _total_ticks; std::uint64_t _ticks_occurred; std::chrono::steady_clock::time_point _begin; ... } 

I would also like to print the remaining time. I found the formula another question , which states that time remains (variable names changed according to my class):

time_left = (time_taken / _total_ticks) * (_total_ticks - _ticks_occured)

The parts I would like to fill out for my class are time_left and time_taken , using the new C ++ 11 <chrono> header.

I know that I need to use std::chrono::steady_clock , but I'm not sure how to integrate it into the code. I guess the best way to measure time would be std::uint64_t as nanoseconds.

My questions:

  • Is there a function in <chrono> that converts nanoseconds to std::string , say something like "3m12s"?
  • Should I use std::chrono::steady_clock::now() every time I update my progress bar and subtract from _begin to determine time_left ?
  • Is there a better algorithm for determining time_left
+6
source share
2 answers

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 ...; -)

+7
source

The chronological library presents types for representing duration. You do not have to convert this to a flat integer of some "known" element. When you want a well-known block to use only types of chronographs, for example. 'std :: chrono :: nanoseconds' and duration_cast . Or create your own type of duration using a floating point view and one of the SI values. For instance. std::chrono::duration<double,std::nano> . Without duration_cast or rounding floating point is prohibited at compile time.

I / O objects for chrono did not make it into C ++ 11, but you can get the source here . Using this, you can simply ignore the duration type and it will print the units you want. I donโ€™t think there is anything there that will show time in minutes, seconds, etc., But this should not be written too hard.

I donโ€™t know that there are too many reasons to worry about calling steady_clock::now() often if this is what you are asking for. I expect that on most platforms there will be a pretty fast timer for this kind of thing. It really depends on the implementation. Obviously, this causes a problem for you, so perhaps you can only call steady_clock::now() inside the if (/* should we update */) block, which should lead to a reasonable limitation of the frequency of calls.

Obviously, there are other ways to estimate the remaining time. For example, instead of averaging this progress (as the formula that you show), you can take the average value from the last N ticks. Or do both and take the weighted average of the two ratings.

+3
source

Source: https://habr.com/ru/post/909859/


All Articles