估计C++ 11中剩下的时间

ner*_*ehl 6 c++ algorithm c++11

我正在编写一个进度条类,每个n刻度都会输出一个更新的进度条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;
  ...
}
Run Code Online (Sandbox Code Playgroud)

我还想输出剩余的时间.我在另一个问题上找到了一个公式,说明剩余的时间是(变量名称已更改为适合我的班级):

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

我想填补了我的课的部分是time_lefttime_taken使用C++ 11的新<chrono>报头.

我知道我需要使用a std::chrono::steady_clock,但我不确定如何将其集成到代码中.我认为测量时间的最佳方法std::uint64_t是纳秒.

我的问题是:

  1. 是否有一个函数<chrono>将纳秒转换成一个std::string像"3m12s"的东西?
  2. 我应该std::chrono::steady_clock::now()每次更新进度条时使用,并从中减去_begin确定time_left吗?
  3. 是否有更好的算法来确定 time_left

How*_*ant 7

是否有一个函数将纳秒转换为std :: string,比如"3m12s"?

不,但我会告诉你如何轻松地在下面这样做.

我每次更新进度条时都应该使用std :: chrono :: steady_clock :: now(),并从_begin中减去它以确定time_left吗?

是.

有没有更好的算法来确定time_left

是.见下文.

编辑

我原本把"滴答声"误解为"时钟滴答",而实际上"滴答"有工作单位,_ticks_occurred/_total_ticks可以解释为%job_done.所以我相应地改变了progress_bar下面的建议.

我相信等式:

time_left = (time_taken / _total_ticks) * (_total_ticks - _ticks_occured)
Run Code Online (Sandbox Code Playgroud)

是不正确的.它没有通过健全性检查:如果_ticks_occured == 1并且_total_ticks很大,则time_left大约等于(好,稍微少)time_taken.这没有意义.

我正在重写上面的等式:

time_left = time_taken * (1/percent_done - 1)
Run Code Online (Sandbox Code Playgroud)

哪里

percent_done = _ticks_occurred/_total_ticks
Run Code Online (Sandbox Code Playgroud)

现在percent_done接近零,time_left接近无穷大,当percent_done接近1时,'time_left接近0.当percent_done是10%时,time_left9*time_taken.这符合我的期望,假设每个工作的时间成本大致是线性的.

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;
  //...
};
Run Code Online (Sandbox Code Playgroud)

只要你可以,就可以在std :: chrono :: durations中进行交易.这样<chrono>做可以为您完成所有转换.typedef可以使用长名称轻松输入.将时间分解为分钟和秒钟就像上面所示一样简单.

正如bames53在他的回答中所说,如果你想使用我的<chrono_io>设施,那也很酷.您的需求可能很简单,您不想这样做.这是一个判断电话.bames53的答案是一个很好的答案.我认为这些额外的细节也可能有所帮助.

编辑

我不小心在上面的代码中留下了一个错误.而不只是修补上面的代码,我认为指出错误并展示如何使用<chrono>它来修复它是一个好主意.

错误在这里:

duration time_left = time_taken * static_cast<rep>(1/percent_done - 1);
Run Code Online (Sandbox Code Playgroud)

和这里:

typedef Clock::duration duration;
Run Code Online (Sandbox Code Playgroud)

在实践steady_clock::duration中通常基于整体类型. <chrono>称之为rep(表示的简称).并且当percent_done大于50%时,乘以的因子time_taken将小于1.并且当rep为积分时,将被转换为0.因此,这progress_bar仅在前50%期间表现良好并且预测在最后50%期间剩余0时间. 50%.

修复此问题的关键是在durations中基于浮点而不是整数进行流量处理.并且这<chrono>使得这很容易做到.

typedef std::chrono::steady_clock Clock;
typedef Clock::time_point time_point;
typedef Clock::period period;
typedef std::chrono::duration<float, period> duration;
Run Code Online (Sandbox Code Playgroud)

duration现在具有相同的刻度周期,steady_clock::duration但使用a float表示.现在计算time_left可以省去static_cast:

duration time_left = time_taken * (1/percent_done - 1);
Run Code Online (Sandbox Code Playgroud)

以下是使用这些修复程序的整个包:

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;
  //...
};
Run Code Online (Sandbox Code Playgroud)

没有什么比一点测试...... ;-)