2012-03-02 52 views
6

Tôi đang viết một lớp thanh tiến trình mà kết quả đầu ra một thanh tiến trình cập nhật mỗi n ve một std::ostream:thời gian Ước tính còn lại trong 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; 
    ... 
} 

Tôi muốn cũng sản lượng thời gian còn lại. Tôi tìm thấy một công thức trên another question đó khẳng định thời gian còn lại được (tên biến thay đổi để phù hợp với lớp học của tôi):

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

Các bộ phận Tôi muốn để điền vào cho lớp học của tôi là time_lefttime_taken, sử dụng C++ 11 mới của tiêu đề <chrono>.

Tôi biết mình cần sử dụng std::chrono::steady_clock, nhưng tôi không chắc chắn cách tích hợp nó vào mã. Tôi cho rằng cách tốt nhất để đo thời gian sẽ là std::uint64_t là nano giây.

Câu hỏi của tôi là:

  1. Có một hàm trong <chrono> rằng sẽ chuyển đổi thành một nano giây std::string, nói điều gì đó như "3m12s"?
  2. Tôi có nên sử dụng std::chrono::steady_clock::now() mỗi khi tôi cập nhật thanh tiến trình của mình và trừ số đó khỏi _begin để xác định time_left?
  3. Có một thuật toán tốt hơn để xác định time_left

Trả lời

7

Có chức năng nào trong đó sẽ chuyển đổi nano giây thành chuỗi std ::, ví dụ như "3m12s"?

No.Nhưng tôi sẽ chỉ cho bạn cách bạn có thể dễ dàng thực hiện việc này bên dưới.

Tôi có nên sử dụng tiêu chuẩn :: chrono :: steady_clock :: now() mỗi lần tôi cập nhật thanh tiến trình của mình và trừ từ _begin để xác định time_left?

Có.

Có một thuật toán tốt hơn để xác định TIME_LEFT

Yes. Xem bên dưới.

Sửa

Ban đầu tôi đã hiểu sai "ve" là "đồng hồ tíc tắc", trong khi thực tế "tíc tắc" có các đơn vị của công việc và _ticks_occurred/_total_ticks thể được hiểu là% job_done. Vì vậy, tôi đã thay đổi đề xuất progress_bar bên dưới cho phù hợp.

tôi tin rằng phương trình:

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

là không chính xác. Nó không vượt qua một kiểm tra sanity: Nếu _ticks_occured == 1_total_ticks là lớn, sau đó time_left xấp xỉ bằng (ok, hơi ít) time_taken. Điều đó không có ý nghĩa.

Tôi viết lại phương trình trên là:

time_left = time_taken * (1/percent_done - 1) 

nơi

percent_done = _ticks_occurred/_total_ticks 

Bây giờ là percent_done tiếp cận zero, time_left tiến tới vô cùng, và khi percent_done tiếp cận 1, 'time_left tiếp cận 0. Khi percent_done là 10%, time_left9*time_taken. Điều này đáp ứng được sự mong đợi của tôi, giả sử chi phí thời gian tuyến tính cho mỗi công việc-tick.

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

Giao thông trong std :: chrono :: thời gian bất cứ khi nào bạn có thể. Bằng cách đó, <chrono> thực hiện tất cả các chuyển đổi cho bạn. typedefs có thể dễ dàng gõ với tên dài. Và chia nhỏ thời gian thành phút và giây dễ dàng như được trình bày ở trên.

Như bames53 ghi chú trong câu trả lời của mình, nếu bạn muốn sử dụng cơ sở <chrono_io> của tôi, điều đó cũng thật tuyệt. Nhu cầu của bạn có thể đủ đơn giản mà bạn không muốn. Đó là một cuộc gọi phán xét. Câu trả lời của bames53 là một câu trả lời hay. Tôi nghĩ những chi tiết bổ sung này cũng có thể hữu ích.

Sửa

tôi vô tình để lại một lỗi trong đoạn code trên. Và thay vì chỉ vá mã ở trên, tôi nghĩ sẽ là một ý hay khi chỉ ra lỗi và chỉ cách sử dụng <chrono> để sửa lỗi.

Lỗi là ở đây:

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

và ở đây:

typedef Clock::duration duration; 

Trong thực tế steady_clock::duration thường dựa trên một loại không thể thiếu. <chrono> gọi đây là số rep (viết tắt là đại diện). Và khi percent_done lớn hơn 50%, hệ số nhân với time_taken sẽ nhỏ hơn 1. Và khi rep là tích phân, được đúc thành 0. Vì vậy, progress_bar chỉ hoạt động tốt trong 50% đầu tiên và dự đoán 0 thời gian còn lại trong 50% cuối cùng.

Chìa khóa để sửa lỗi này là lưu lượng truy cập trong duration s dựa trên điểm động thay vì số nguyên. Và <chrono> làm cho việc này rất dễ thực hiện.

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

duration nay đã có khoảng thời gian đánh dấu giống như steady_clock::duration nhưng sử dụng một float cho đại diện. Và bây giờ tính toán cho time_left có thể rời khỏi static_cast:

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

Dưới đây là toàn bộ gói lại với các bản sửa lỗi:

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

Không có gì giống như một thử nghiệm nhỏ ... ;-)

+0

Lý do cho "đánh dấu" là đại diện cho tiến độ, tức là: Tôi có 1.000.000 hoạt động (bọ ve) để hoàn thành. Từ những gì tôi có thể nói, boost có 'chrono_io'. Bạn có đề nghị sử dụng nó không? – nerozehl

+1

Ồ, "đánh dấu" đại diện cho một đơn vị công việc, không phải là một đơn vị thời gian? Nếu vậy, tôi hiểu lầm câu hỏi của bạn. Tôi nhầm lẫn đọc "đồng hồ đánh dấu". Tăng cường ' 'là bản sao được ủy quyền của' '. Nó sẽ là tốt để sử dụng nếu bạn thấy nó hữu ích. Tôi đang đề xuất ' 'cho một tiêu chuẩn trong tương lai, vì vậy sẽ rất tốt khi có phản hồi về nó. –

+0

Có đánh dấu là một đơn vị công việc .. Tôi cho rằng tôi nên rõ ràng hơn, tôi xin lỗi vì những rắc rối về điều đó. Cảm ơn nhiều! :) – nerozehl

3

Thư viện chrono bao gồm các loại cho đại diện cho khoảng thời gian. Bạn không nên chuyển đổi thành số nguyên phẳng của một số đơn vị 'đã biết'. Khi bạn muốn một đơn vị đã biết chỉ sử dụng các loại chrono, ví dụ: 'std :: chrono :: nanoseconds' và duration_cast. Hoặc tạo ra loại thời gian của riêng bạn bằng cách sử dụng một đại diện điểm nổi và một trong các tỷ lệ SI. Ví dụ. std::chrono::duration<double,std::nano>. Nếu không có duration_cast hoặc thời gian làm tròn dấu chấm động bị cấm tại thời gian biên dịch.

Các cơ sở IO cho chrono đã không biến nó thành C++ 11, nhưng bạn có thể lấy nguồn từ here. Sử dụng điều này bạn chỉ có thể bỏ qua các loại thời gian, và nó sẽ in các đơn vị phải. Tôi không nghĩ rằng có bất cứ điều gì ở đó sẽ hiển thị thời gian trong vài phút, giây, vv, nhưng một điều như vậy không nên quá khó để viết.

Tôi không biết rằng có quá nhiều lý do để quan tâm đến việc gọi số steady_clock::now() thường xuyên, nếu đó là những gì bạn yêu cầu. Tôi hy vọng hầu hết các nền tảng sẽ có một bộ hẹn giờ khá nhanh chỉ với những thứ như vậy. Nó phụ thuộc vào việc thực hiện mặc dù. Rõ ràng nó gây ra sự cố cho bạn, vì vậy có thể bạn chỉ có thể gọi steady_clock::now() bên trong khối if (/* should we update */), điều này sẽ đặt giới hạn hợp lý về tần suất cuộc gọi.

Rõ ràng có nhiều cách khác để ước tính thời gian còn lại. Ví dụ thay vì lấy mức trung bình so với tiến độ cho đến nay (đó là công thức mà bạn trình bày), bạn có thể lấy mức trung bình từ N lần cuối cùng. Hoặc làm cả hai và lấy trọng số trung bình của hai ước tính.

+0

Would bạn khuyên bạn nên sử dụng boost :: chrono over std :: chrono? – nerozehl

+0

boost :: chrono không có thêm các thiết bị IO, và độ phân giải của nó trên Windows là tốt hơn so với các thư viện trong bản beta VS11. Nhưng đồng thời tôi thích sử dụng tiêu chuẩn bất cứ khi nào có thể. Tôi cũng không thấy vấn đề gì cả. – bames53

Các vấn đề liên quan