2012-03-02 25 views
6

我正在写一个进度条类,每n蜱输出更新进度条到std::ostream留在估算时间C++ 11

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

我想也输出的剩余时间。我发现了一个公式上another question,指出剩余的时间(变量名称更改为适合我的课):

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

我想,以填补在我的课的部分是time_lefttime_taken,用C++ 11的新<chrono>标题。

我知道我需要使用std::chrono::steady_clock,但我不确定如何将它集成到代码中。我假设测量时间的最佳方式是以纳秒为单位的std::uint64_t

我的问题是:

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

回答

7

有没有函数可以将纳秒转换为 std :: string,比如说“3m12s”?

No.但我会告诉你如何轻松地做到这一点。

我应该使用std ::计时:: steady_clock ::现在()每次我更新 我的进度条,并减去从_begin确定TIME_LEFT?

是的。

是否有更好的算法来确定TIME_LEFT

是。见下文。

编辑

我本来误解“滴答”为“时钟滴答”,当在现实中“滴答”有工作的单位和_ticks_occurred/_total_ticks可以解释为%JOB_DONE。所以我已经相应地改变了建议的progress_bar

我相信公式:

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

不正确。它没有通过一个完整性检查:如果_ticks_occured == 1_total_ticks很大,那么time_left大约等于(好,略少)time_taken。这没有意义。

我重写上述方程为:

time_left = time_taken * (1/percent_done - 1) 

其中

percent_done = _ticks_occurred/_total_ticks 

现在,作为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; 
    //... 
}; 

只要你可以在std :: chrono :: durations中进行交通。那样<chrono>为你做了所有的转换。 typedefs可以减少长名称的打字。把时间分成几分钟和几秒就像上面显示的一样简单。

由于bames53在他的答案中提到,如果你想使用我的<chrono_io>设施,那也很酷。你的需求可能很简单,你不想。这是一个判断呼吁。 bames53的回答是一个很好的答案。我认为这些额外的细节可能也有帮助。

编辑

我意外地留的错误在上面的代码。而不是仅仅修补上面的代码,我认为指出错误并展示如何使用<chrono>来修复它是个好主意。

的错误是在这里:

duration time_left = time_taken * static_cast<rep>(1/percent_done - 1); 

这里:

typedef Clock::duration duration; 

在实践steady_clock::duration通常基于整型。 <chrono>称之为rep表示的简写)。并且当percent_done大于50%时,乘以time_taken的因子将小于1.并且当rep是整数时,该因子被转换为0.因此,这个progress_bar在前50%期间仅表现良好并且预测0时间在过去的50%左右。

解决这个问题的关键在于基于浮点数而不是整数的流量duration<chrono>使这非常容易做到。

typedef std::chrono::steady_clock Clock; 
typedef Clock::time_point time_point; 
typedef Clock::period period; 
typedef std::chrono::duration<float, period> duration; 

duration现在具有相同的刻度周期作为steady_clock::duration但使用float用于表示。这些修复再次

duration time_left = time_taken * (1/percent_done - 1); 

这里是全包:现在对于time_left计算可以不指定的static_cast

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; 
    //... 
}; 

没有什么像一个小测试... ;-)

+0

“打勾”的原因是为了表示进度,即:我有100万次操作(滴答)完成。从我可以告诉助推有一个'chrono_io'。你会推荐使用它吗? – nerozehl 2012-03-03 00:26:41

+1

哦,“嘀嗒”代表一个工作单位,而不是时间单位?如果是这样,我误解了你的问题。我误读了“时钟滴答”。增强''是我的''的授权副本。如果您觉得有帮助,应该可以使用。对于未来的标准,我建议'',所以最好有反馈意见。 – 2012-03-03 00:43:13

+0

是蜱是一个工作单位..我想我应该更清楚,我对此感到抱歉。非常感谢你! :) – nerozehl 2012-03-03 00:52:43

3

计时库包括类型代表的持续时间。你不应该将它转换为某个“已知”单位的平整整数。当你想要一个已知的单位只使用计时类型,例如'std :: chrono :: nanoseconds'和duration_cast。或者使用浮点表示法和一个SI比率创建您自己的持续时间类型。例如。 std::chrono::duration<double,std::nano>。如果没有duration_cast或在编译时禁止浮点时间四舍五入。

chrono的IO设备并未将其转换为C++ 11,但您可以从here获取源代码。使用这个,你可以忽略持续时间类型,它会打印正确的单位。我认为没有什么能够以分钟,秒等方式显示时间,但这样的事情不应该太难写。

我不知道有太多的理由经常打电话给 steady_clock::now(),如果这就是你的要求。我希望大多数平台都有一个相当快的计时器来处理这种事情。它确实取决于实施。 显然这对您造成了一个问题,因此您可能只能在if (/* should we update */)区块内拨打steady_clock::now(),这应该对呼叫频率设置合理的限制。

显然还有其他方法来估计剩余时间。例如,不是取得迄今为止的平均值(这是您所展示的公式的平均值),您可以取最后N个滴答的平均值。或者做两个并且采取两个估计的一个加权平均。

+0

会你推荐使用boost :: chrono over std :: chrono? – nerozehl 2012-03-03 00:27:36

+0

boost :: chrono确实有额外的IO设施,其在Windows上的分辨率要好于VS11测试版中的库。但同时我喜欢尽可能使用标准。我没有看到任何问题。 – bames53 2012-03-03 02:21:45