2013-04-01 40 views
5

Tôi đang cố sử dụng chrono::steady_clock để đo số giây phân đoạn đã trôi qua giữa một khối mã trong chương trình của tôi. Tôi có khối mã này làm việc trong LiveWorkSpace (http://liveworkspace.org/code/YT1I$9):Đo kết quả thời gian theo giá trị trả về là 0 hoặc 0.001

#include <chrono> 
#include <iostream> 
#include <vector> 

int main() 
{ 
    auto start = std::chrono::steady_clock::now(); 
    for (unsigned long long int i = 0; i < 10000; ++i) { 
     std::vector<int> v(i, 1); 
    } 
    auto end = std::chrono::steady_clock::now(); 

    auto difference = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count(); 

    std::cout << "seconds since start: " << ((double)difference/1000000); 
} 

Khi tôi thực hiện ý tưởng tương tự vào chương trình của tôi như vậy:

auto start = std::chrono::steady_clock::now(); 
// block of code to time 
auto end = std::chrono::stead_clock::now(); 

auto difference = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() 

std::cout << "seconds since start: " << ((double) difference/1000000); 

Chương trình sẽ chỉ in ra giá trị của 00.001 . Tôi rất nghi ngờ rằng thời gian thực thi cho khối mã của tôi luôn bằng 0 hoặc 1000 micro giây, vậy điều gì là kế toán cho làm tròn này và làm cách nào để loại bỏ nó để tôi có thể nhận được các giá trị phân số thích hợp?

Đây là chương trình Windows.

+0

Thay vào đó hãy thử 'std :: chrono :: high_resolution_clock'. –

+0

@KerrekSB: Đó là một gợi ý tốt, nhưng 'steady_clock :: now()' nên đã chính xác đến nano giây, hoặc ít nhất là ve (khoảng 100 nano giây). Tôi nghi ngờ có một vấn đề toán học ở đây. –

+0

@RobertHarvey: Ồ, có lẽ các dấu ngoặc đơn bị thất lạc trong dàn diễn viên? –

Trả lời

2

Sau khi chạy một số thử nghiệm trên MSVC2012, tôi có thể xác nhận rằng đồng hồ C++ 11 trong triển khai của Microsoft không có độ phân giải đủ cao. Xem C++ header's high_resolution_clock does not have high resolution để biết báo cáo lỗi liên quan đến vấn đề này.

Vì vậy, không may cho một bộ đếm thời độ phân giải cao hơn, bạn sẽ cần phải sử dụng boost::chrono hoặc QueryPerformanceCounter trực tiếp như vậy cho đến khi họ sửa chữa các lỗi:

#include <iostream> 
#include <Windows.h> 

int main() 
{ 
    LARGE_INTEGER frequency; 
    QueryPerformanceFrequency(&frequency); 

    LARGE_INTEGER start; 
    QueryPerformanceCounter(&start); 

    // Put code here to time 

    LARGE_INTEGER end; 
    QueryPerformanceCounter(&end); 

    // for microseconds use 1000000.0 
    double interval = static_cast<double>(end.QuadPart- start.QuadPart)/
         frequency.QuadPart; // in seconds 
    std::cout << interval; 
} 
+0

Ah, không có gì lạ khi nó không hoạt động đúng cách. Tôi muốn hiểu thêm về hàm 'QueryPerformanceFrequency()' và nó thực sự đang làm gì. Tôi có nên luôn gọi hàm đó trước khi tôi khởi động bộ hẹn giờ hay đó là chức năng chỉ cần được gọi một lần trong chương trình? Chúng tôi đã chia tần số chính xác chưa? Các kết quả hiện đang cho tôi vài giây trong hàng trăm, điều này dường như không chính xác. – raphnguyen

+0

@raphnguyen: 'QueryPerformanceFrequency' chỉ nên được gọi một lần trong chương trình để nhận tần suất. Ví dụ tôi đã hiển thị là trong 'micro giây', nếu bạn muốn giây loại bỏ '1000000.0'. Tôi đã đổi mã thành giây. –

+0

@raphnguyen: 'QueryPerformanceFrequency' trả về tần suất tính trên giây, vì vậy bạn nếu bạn chia số đếm cho số đó, bạn sẽ nhận được số giây. –

5

Câu hỏi này đã có một câu trả lời tốt. Nhưng tôi muốn thêm đề xuất khác:

Làm việc trong khuôn khổ <chrono>. Xây dựng đồng hồ của riêng bạn. Xây dựng time_point của riêng bạn. Xây dựng thời lượng của riêng bạn. Khung <chrono> rất tùy chỉnh. Bằng cách làm việc trong hệ thống đó, bạn sẽ không chỉ tìm hiểu std::chrono, nhưng khi nhà cung cấp của bạn bắt đầu vận chuyển đồng hồ bạn hài lòng, nó sẽ là tầm thường để chuyển mã của bạn từ chrono tay của bạn :: clock to std::high_resolution_clock (hoặc bất kỳ).

Đầu tiên, mặc dù một lời chỉ trích nhỏ về mã ban đầu của bạn:

std::cout << "seconds since start: " << ((double) difference/1000000); 

Bất cứ khi nào bạn thấy mình giới thiệu hằng số chuyển đổi (như 1000000) để có được những gì bạn muốn, bạn không sử dụng một cách chính xác chrono. Mã của bạn không chính xác, chỉ mong manh. Bạn có phải là chắc chắn bạn có đúng số không trong hằng số đó không ?!

Ngay cả trong ví dụ đơn giản này, bạn nên nói với chính mình:

Tôi muốn nhìn thấy đầu ra về giây đại diện bởi một đôi.

Và sau đó bạn nên sử dụng chrono làm điều đó cho bạn. Nó rất dễ dàng khi bạn tìm hiểu cách làm:

typedef std::chrono::duration<double> sec; 
sec difference = end - start; 
std::cout << "seconds since start: " << difference.count() << '\n'; 

Dòng đầu tiên tạo ra một loại với khoảng thời gian 1 giây, được biểu thị bằng một đôi.

Dòng thứ hai chỉ đơn giản là trừ các mốc thời gian của bạn và gán nó vào loại thời lượng tùy chỉnh của bạn. Việc chuyển đổi từ các đơn vị của steady_clock::time_point sang thời lượng tùy chỉnh của bạn (một giây đôi) được thực hiện tự động bởi thư viện chrono.Điều này đơn giản hơn nhiều:

auto difference = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() 

Và cuối cùng bạn chỉ cần in kết quả của mình bằng hàm thành viên .count(). Đây lại là đơn giản hơn nhiều so với:

std::cout << "seconds since start: " << ((double) difference/1000000); 


Nhưng kể từ khi bạn không hài lòng với độ chính xác của std::chrono::steady_clock, và bạn có quyền truy cập vào QueryPerformanceCounter, bạn có thể làm tốt hơn. Bạn có thể xây dựng đồng hồ của riêng mình trên đầu trang của QueryPerformanceCounter.

<disclaimer>

Tôi không có hệ thống Windows để kiểm tra mã sau.

</disclaimer>

struct my_clock 
{ 
    typedef double        rep; 
    typedef std::ratio<1>      period; 
    typedef std::chrono::duration<rep, period> duration; 
    typedef std::chrono::time_point<my_clock> time_point; 
    static const bool is_steady =    false; 

    static time_point now() 
    { 
     static const long long frequency = init_frequency(); 
     long long t; 
     QueryPerformanceCounter(&t); 
     return time_point(duration(static_cast<rep>(t)/frequency)); 
    } 
private: 
    static long long init_frequency() 
    { 
     long long f; 
     QueryPerformanceFrequency(&f); 
     return f; 
    } 
}; 

Vì bạn muốn đầu ra của bạn trong điều kiện của một giây đôi, tôi đã thực hiện các rep của đồng hồ này một doubleperiod 1 giây. Bạn có thể dễ dàng tạo ra tích phân repperiod một số đơn vị khác như micro giây hoặc nano giây. Bạn chỉ cần điều chỉnh typedef s và chuyển đổi từ QueryPerformanceCounter thành duration trong now().

Và bây giờ mã của bạn có thể trông giống như mã ban đầu của bạn:

int main() 
{ 
    auto start = my_clock::now(); 
    for (unsigned long long int i = 0; i < 10000; ++i) { 
     std::vector<int> v(i, 1); 
    } 
    auto end = my_clock::now(); 

    auto difference = end - start; 
    std::cout << "seconds since start: " << difference.count() << '\n'; 
} 

Nhưng mà không các hằng số chuyển đổi tay mã cố định và với (những gì tôi hy vọng là) đủ chính xác cho bạn nhu cầu. Và với đường dẫn port dễ dàng hơn để triển khai std::chrono::steady_clock trong tương lai.

<chrono> được thiết kế để trở thành thư viện có thể mở rộng. Vui lòng mở rộng. :-)

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