Chức năng hẹn giờ để cung cấp thời gian tính bằng nano giây bằng C ++


101

Tôi muốn tính thời gian cần thiết để API trả về một giá trị. Thời gian thực hiện cho một hành động như vậy tính bằng nano giây. Vì API là một lớp / hàm C ++, tôi đang sử dụng timer.h để tính toán giống nhau:

  #include <ctime>
  #include <cstdio>

  using namespace std;

  int main(int argc, char** argv) {

      clock_t start;
      double diff;
      start = clock();
      diff = ( std::clock() - start ) / (double)CLOCKS_PER_SEC;
      cout<<"printf: "<< diff <<'\n';

      return 0;
  }

Đoạn mã trên cho thời gian tính bằng giây. Làm cách nào để tôi có được điều tương tự trong nano giây và chính xác hơn?


sẽ tính toán mã trên trong vài giây, tôi muốn có được câu trả lời trong nano giây ...
gagneet

Cần thêm nền tảng vào câu hỏi (và tốt nhất là cả tiêu đề) để có câu trả lời tốt.
Patrick Johnmeyer

Ngoài ra, để có được thời gian, người ta cần phải tìm kiếm các vấn đề với microbenchmarking (điều này cực kỳ phức tạp) - chỉ thực hiện một lần thực hiện và lấy thời gian ở đầu và cuối, không có khả năng cung cấp đủ độ chính xác.
Blaisorblade,

@Blaisorblade: Đặc biệt là vì tôi đã phát hiện ra trong một số thử nghiệm của mình, tốc độ clock()gần như không nhanh như tôi nghĩ.
Mooing Duck,

Câu trả lời:


83

Những gì những người khác đã đăng về việc chạy hàm lặp đi lặp lại trong một vòng lặp là đúng.

Đối với Linux (và BSD), bạn muốn sử dụng clock_gettime () .

#include <sys/time.h>

int main()
{
   timespec ts;
   // clock_gettime(CLOCK_MONOTONIC, &ts); // Works on FreeBSD
   clock_gettime(CLOCK_REALTIME, &ts); // Works on Linux
}

Đối với các cửa sổ bạn muốn sử dụng QueryPerformanceCounter . Và đây là nhiều hơn về QPC

Rõ ràng có một vấn đề đã biết với QPC trên một số chipset, vì vậy bạn có thể muốn đảm bảo rằng mình không có chipset đó. Ngoài ra, một số AMD lõi kép cũng có thể gây ra sự cố . Xem bài đăng thứ hai của sebbbi, nơi anh ấy nói:

QueryPerformanceCounter () và QueryPerformanceFrequency () cung cấp độ phân giải tốt hơn một chút, nhưng có các vấn đề khác nhau. Ví dụ: trong Windows XP, tất cả CPU lõi kép AMD Athlon X2 trả về máy tính của một trong hai lõi "ngẫu nhiên" (PC đôi khi nhảy ngược một chút), trừ khi bạn cài đặt đặc biệt gói trình điều khiển lõi kép AMD để khắc phục sự cố. Chúng tôi đã không nhận thấy bất kỳ CPU lõi kép nào khác gặp sự cố tương tự (p4 kép, p4 ht, lõi2 kép, lõi2 quad, phenom quad).

CHỈNH SỬA 2013/07/16:

Có vẻ như có một số tranh cãi về hiệu quả của QPC trong một số trường hợp nhất định như đã nêu trong http://msdn.microsoft.com/en-us/library/windows/desktop/ee417693(v=vs.85).aspx

... Mặc dù QueryPerformanceCounter và QueryPerformanceFrequency thường điều chỉnh cho nhiều bộ xử lý, các lỗi trong BIOS hoặc trình điều khiển có thể dẫn đến việc các quy trình này trả về các giá trị khác nhau khi luồng di chuyển từ bộ xử lý này sang bộ xử lý khác ...

Tuy nhiên câu trả lời StackOverflow này https://stackoverflow.com/a/4588605/34329 nói rằng QPC sẽ hoạt động tốt trên bất kỳ hệ điều hành MS nào sau gói dịch vụ Win XP 2.

Bài viết này cho thấy rằng Windows 7 có thể xác định xem (các) bộ xử lý có TSC bất biến và rơi trở lại bộ hẹn giờ bên ngoài nếu chúng không. http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html Đồng bộ hóa giữa các bộ xử lý vẫn là một vấn đề.

Đọc tốt khác liên quan đến bộ đếm thời gian:

Xem các bình luận để biết thêm chi tiết.


1
Tôi đã thấy xung nhịp TSC bị lệch trên PC Xeon kép cũ hơn, nhưng gần như không tệ như trên Athlon X2 có bật xung nhịp C1. Với xung nhịp C1, việc thực thi một lệnh HLT làm chậm xung nhịp, khiến TSC trên các lõi nhàn rỗi tăng chậm hơn so với các lõi hoạt động.
bk1e

6
CLOCK_MONOTONIC hoạt động trên các phiên bản Linux mà tôi có sẵn.
Bernard

1
@Bernard - Điều đó phải được thêm mới kể từ lần cuối tôi xem xét cái này. Cảm ơn cho những người đứng đầu lên.
đau buồn

3
Trên thực tế, bạn phải sử dụng CLOCK_MONOTONIC_RAW, nếu nó có sẵn, để có được thời gian phần cứng không bị NTP điều chỉnh.

Như đã thảo luận ở đây, thực hiện đúng các QPC không sử dụng bộ đếm TSC, ít nhất, nơi nó được biết đến là không đáng tin cậy: stackoverflow.com/q/510462/53974
Blaisorblade

69

Câu trả lời mới này sử dụng <chrono>cơ sở của C ++ 11 . Trong khi có các câu trả lời khác hiển thị cách sử dụng <chrono>, không câu trả lời nào trong số đó chỉ ra cách sử dụng <chrono>với RDTSCcơ sở được đề cập trong một số câu trả lời khác ở đây. Vì vậy, tôi nghĩ tôi sẽ chỉ cách sử dụng RDTSCvới <chrono>. Thêm vào đó, tôi sẽ chứng minh làm thế nào bạn có thể templatize mã thử nghiệm trên đồng hồ để bạn có thể nhanh chóng chuyển đổi giữa RDTSCvà hệ thống của bạn được xây dựng trong các cơ sở đồng hồ (trong đó có khả năng sẽ được dựa trên clock(), clock_gettime()và / hoặcQueryPerformanceCounter .

Lưu ý rằng RDTSChướng dẫn dành riêng cho x86. QueryPerformanceCounterchỉ dành cho Windows. Và clock_gettime()chỉ có POSIX. Dưới đây tôi giới thiệu hai đồng hồ mới:std::chrono::high_resolution_clockstd::chrono::system_clock, nếu bạn có thể giả sử C ++ 11, hiện là đa nền tảng.

Đầu tiên, đây là cách bạn tạo xung nhịp tương thích với C ++ 11 từ rdtschướng dẫn lắp ráp Intel . Tôi sẽ gọi nó là x::clock:

#include <chrono>

namespace x
{

struct clock
{
    typedef unsigned long long                 rep;
    typedef std::ratio<1, 2'800'000'000>       period; // My machine is 2.8 GHz
    typedef std::chrono::duration<rep, period> duration;
    typedef std::chrono::time_point<clock>     time_point;
    static const bool is_steady =              true;

    static time_point now() noexcept
    {
        unsigned lo, hi;
        asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
        return time_point(duration(static_cast<rep>(hi) << 32 | lo));
    }
};

}  // x

Tất cả những gì xung nhịp này làm là đếm chu kỳ CPU và lưu trữ nó trong một số nguyên 64-bit không dấu. Bạn có thể cần phải chỉnh sửa cú pháp hợp ngữ cho trình biên dịch của mình. Hoặc trình biên dịch của bạn có thể cung cấp một nội tại mà bạn có thể sử dụng thay thế (ví dụ now() {return __rdtsc();}:).

Để xây dựng một chiếc đồng hồ, bạn phải cung cấp cho nó đại diện (kiểu lưu trữ). Bạn cũng phải cung cấp chu kỳ đồng hồ, phải là một hằng số thời gian biên dịch, mặc dù máy của bạn có thể thay đổi tốc độ đồng hồ ở các chế độ năng lượng khác nhau. Và từ những thứ đó, bạn có thể dễ dàng xác định khoảng thời gian và thời điểm "gốc" của đồng hồ về các nguyên tắc cơ bản này.

Nếu tất cả những gì bạn muốn làm là xuất ra số lần tích tắc đồng hồ, thì việc bạn đưa ra con số nào cho chu kỳ đồng hồ không thực sự quan trọng. Hằng số này chỉ phát huy tác dụng nếu bạn muốn chuyển đổi số tích tắc đồng hồ thành một số đơn vị thời gian thực chẳng hạn như nano giây. Và trong trường hợp đó, bạn có khả năng cung cấp tốc độ đồng hồ càng chính xác thì việc chuyển đổi thành nano giây, (mili giây, bất kỳ) càng chính xác.

Dưới đây là mã ví dụ hiển thị cách sử dụng x::clock. Trên thực tế, tôi đã tạo mẫu mã trên đồng hồ để chỉ cách bạn có thể sử dụng nhiều đồng hồ khác nhau với cùng một cú pháp. Thử nghiệm cụ thể này cho thấy chi phí lặp lại khi chạy những gì bạn muốn theo thời gian trong một vòng lặp:

#include <iostream>

template <class clock>
void
test_empty_loop()
{
    // Define real time units
    typedef std::chrono::duration<unsigned long long, std::pico> picoseconds;
    // or:
    // typedef std::chrono::nanoseconds nanoseconds;
    // Define double-based unit of clock tick
    typedef std::chrono::duration<double, typename clock::period> Cycle;
    using std::chrono::duration_cast;
    const int N = 100000000;
    // Do it
    auto t0 = clock::now();
    for (int j = 0; j < N; ++j)
        asm volatile("");
    auto t1 = clock::now();
    // Get the clock ticks per iteration
    auto ticks_per_iter = Cycle(t1-t0)/N;
    std::cout << ticks_per_iter.count() << " clock ticks per iteration\n";
    // Convert to real time units
    std::cout << duration_cast<picoseconds>(ticks_per_iter).count()
              << "ps per iteration\n";
}

Điều đầu tiên mã này làm là tạo đơn vị "thời gian thực" để hiển thị kết quả. Tôi đã chọn pico giây, nhưng bạn có thể chọn bất kỳ đơn vị nào bạn thích, dựa trên tích phân hoặc dấu phẩy động. Ví dụ, có một std::chrono::nanosecondsđơn vị làm sẵn mà tôi có thể đã sử dụng.

Như một ví dụ khác, tôi muốn in ra số chu kỳ đồng hồ trung bình trên mỗi lần lặp dưới dạng dấu phẩy động, vì vậy tôi tạo một khoảng thời gian khác, dựa trên double, có cùng đơn vị với dấu của đồng hồ (được gọi Cycletrong mã).

Vòng lặp được tính thời gian với các cuộc gọi đến clock::now()ở hai bên. Nếu bạn muốn đặt tên cho kiểu được trả về từ hàm này, nó là:

typename clock::time_point t0 = clock::now();

(như được hiển thị rõ ràng trong x::clockví dụ và cũng đúng với đồng hồ do hệ thống cung cấp).

Để có được thời lượng về mặt đồng hồ dấu phẩy động, người ta chỉ cần trừ hai điểm thời gian và để nhận giá trị mỗi lần lặp, hãy chia khoảng thời gian đó cho số lần lặp.

Bạn có thể nhận được số lượng trong bất kỳ khoảng thời gian nào bằng cách sử dụng count()chức năng thành viên. Điều này trả về đại diện bên trong. Cuối cùng, tôi sử dụng std::chrono::duration_castđể chuyển đổi thời lượng Cyclethành thời lượng picosecondsvà in nó ra.

Để sử dụng mã này rất đơn giản:

int main()
{
    std::cout << "\nUsing rdtsc:\n";
    test_empty_loop<x::clock>();

    std::cout << "\nUsing std::chrono::high_resolution_clock:\n";
    test_empty_loop<std::chrono::high_resolution_clock>();

    std::cout << "\nUsing std::chrono::system_clock:\n";
    test_empty_loop<std::chrono::system_clock>();
}

Ở trên, tôi thực hiện bài kiểm tra bằng cách sử dụng đồng hồ tự chế của chúng tôi x::clockvà so sánh các kết quả đó với việc sử dụng hai trong số các đồng hồ do hệ thống cung cấp: std::chrono::high_resolution_clockstd::chrono::system_clock. Đối với tôi điều này in ra:

Using rdtsc:
1.72632 clock ticks per iteration
616ps per iteration

Using std::chrono::high_resolution_clock:
0.620105 clock ticks per iteration
620ps per iteration

Using std::chrono::system_clock:
0.00062457 clock ticks per iteration
624ps per iteration

Điều này cho thấy rằng mỗi đồng hồ này có khoảng thời gian tích tắc khác nhau, vì số tích tắc trên mỗi lần lặp lại rất khác nhau đối với mỗi đồng hồ. Tuy nhiên, khi chuyển đổi sang một đơn vị thời gian đã biết (ví dụ: pico giây), tôi nhận được kết quả gần giống nhau cho mỗi đồng hồ (số dặm của bạn có thể thay đổi).

Lưu ý rằng mã của tôi hoàn toàn không có "hằng số chuyển đổi ma thuật". Thật vậy, chỉ có hai con số kỳ diệu trong toàn bộ ví dụ:

  1. Tốc độ đồng hồ của máy tôi để xác định x::clock.
  2. Số lần lặp lại để kiểm tra. Nếu việc thay đổi con số này làm cho kết quả của bạn thay đổi đáng kể, thì bạn có thể nên làm cho số lần lặp lại cao hơn hoặc làm trống máy tính của các quy trình cạnh tranh trong khi thử nghiệm.

5
Bởi "RDTSC là chỉ dành cho Intel", bạn thực sự đang đề cập đến kiến ​​trúc x86 và các dẫn xuất, phải không? Các chip AMD, Cyrix, Transmeta x86 có hướng dẫn , còn các bộ xử lý Intel RISC và ARM thì không.
Ben Voigt

1
@BenVoigt: +1 Vâng, phần sửa của bạn khá chính xác, cảm ơn bạn.
Howard Hinnant,

1
Điều chỉnh CPU sẽ ảnh hưởng đến điều này như thế nào? Tốc độ đồng hồ không thay đổi dựa trên tải cpu?
Tejas Kale

@TejasKale: Điều này được mô tả trong câu trả lời trong hai đoạn liên tiếp bắt đầu bằng "Để chế tạo một chiếc đồng hồ bạn ...". Thông thường, mã thời gian không đo lường công việc chặn một luồng (nhưng nó có thể). Và do đó thường CPU của bạn sẽ không hoạt động. Nhưng nếu bạn đang đo mã liên quan đến chế độ ngủ, khóa mutex, chờ điều kiện_variable, v.v., rdtscđồng hồ có thể có chuyển đổi không chính xác sang các đơn vị khác. Bạn nên thiết lập số đo để có thể dễ dàng thay đổi và so sánh đồng hồ (như trong câu trả lời này).
Howard Hinnant

27

Với mức độ chính xác đó, sẽ tốt hơn nếu lý luận trong tích tắc CPU hơn là trong lệnh gọi hệ thống như clock () . Và đừng quên rằng nếu phải mất hơn một nano giây để thực hiện một lệnh ... thì việc đạt được độ chính xác nano giây là điều không thể.

Tuy nhiên, một cái gì đó như vậy là một sự khởi đầu:

Đây là mã thực tế để truy xuất số lần đánh dấu xung nhịp CPU 80x86 đã chuyển kể từ khi CPU được khởi động lần cuối. Nó sẽ hoạt động trên Pentium trở lên (386/486 không được hỗ trợ). Mã này thực sự là dành riêng cho MS Visual C ++, nhưng có thể rất dễ dàng được chuyển sang bất kỳ thứ gì khác, miễn là nó hỗ trợ lắp ráp nội tuyến.

inline __int64 GetCpuClocks()
{

    // Counter
    struct { int32 low, high; } counter;

    // Use RDTSC instruction to get clocks count
    __asm push EAX
    __asm push EDX
    __asm __emit 0fh __asm __emit 031h // RDTSC
    __asm mov counter.low, EAX
    __asm mov counter.high, EDX
    __asm pop EDX
    __asm pop EAX

    // Return result
    return *(__int64 *)(&counter);

}

Chức năng này cũng có ưu điểm là cực kỳ nhanh - thường mất không quá 50 chu kỳ cpu để thực thi.

Sử dụng Hình thời gian :
Nếu bạn cần chuyển số đồng hồ thành thời gian đã trôi qua thực, hãy chia kết quả cho tốc độ đồng hồ của chip của bạn. Hãy nhớ rằng tốc độ "đánh giá" của GHz có thể hơi khác so với tốc độ thực tế của chip của bạn. Để kiểm tra tốc độ thực sự của chip, bạn có thể sử dụng một số tiện ích rất tốt hoặc lệnh gọi Win32, QueryPerformanceFrequency ().


cảm ơn vì thông tin này hữu ích tôi không nghĩ đến các chu kỳ cpu để tính toán thời gian, tôi nghĩ đó là một điểm rất tốt cần ghi nhớ :-)
gagneet

4
Việc sử dụng QueryPerformanceFrequency () để biến số TSC thành thời gian đã trôi qua có thể không hoạt động. QueryPerformanceCounter () sử dụng HPET (Bộ hẹn giờ sự kiện có độ chính xác cao) trên Vista khi có sẵn. Nó sử dụng bộ đếm thời gian quản lý nguồn ACPI nếu người dùng thêm / USEPMTIMER vào boot.ini.
bk1e

23

Để làm điều này một cách chính xác, bạn có thể sử dụng một trong hai cách, đi cùng RDTSChoặc đi cùng clock_gettime(). Thứ hai là nhanh hơn khoảng 2 lần và có lợi thế là cho đúng thời gian tuyệt đối. Lưu ý rằng RDTSCđể hoạt động chính xác, bạn cần sử dụng nó như đã chỉ ra (các nhận xét khác trên trang này có lỗi và có thể mang lại giá trị thời gian không chính xác trên một số bộ xử lý nhất định)

inline uint64_t rdtsc()
{
    uint32_t lo, hi;
    __asm__ __volatile__ (
      "xorl %%eax, %%eax\n"
      "cpuid\n"
      "rdtsc\n"
      : "=a" (lo), "=d" (hi)
      :
      : "%ebx", "%ecx" );
    return (uint64_t)hi << 32 | lo;
}

và đối với clock_gettime: (Tôi đã chọn độ phân giải micro giây một cách tùy ý)

#include <time.h>
#include <sys/timeb.h>
// needs -lrt (real-time lib)
// 1970-01-01 epoch UTC time, 1 mcs resolution (divide by 1M to get time_t)
uint64_t ClockGetTime()
{
    timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    return (uint64_t)ts.tv_sec * 1000000LL + (uint64_t)ts.tv_nsec / 1000LL;
}

thời gian và giá trị được tạo ra:

Absolute values:
rdtsc           = 4571567254267600
clock_gettime   = 1278605535506855

Processing time: (10000000 runs)
rdtsc           = 2292547353
clock_gettime   = 1031119636

22

Tôi đang sử dụng những thứ sau để có được kết quả mong muốn:

#include <time.h>
#include <iostream>
using namespace std;

int main (int argc, char** argv)
{
    // reset the clock
    timespec tS;
    tS.tv_sec = 0;
    tS.tv_nsec = 0;
    clock_settime(CLOCK_PROCESS_CPUTIME_ID, &tS);
    ...
    ... <code to check for the time to be put here>
    ...
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tS);
    cout << "Time taken is: " << tS.tv_sec << " " << tS.tv_nsec << endl;

    return 0;
}

2
Tôi đã phản đối vì cố gắng áp dụng mã này, trước tiên tôi phải lên google lý do tại sao timepec không được xác định. Sau đó, tôi phải google cái gì là POSIX ... và như tôi đã hiểu, mã này không liên quan đến người dùng Windows, những người cần gắn bó với thư viện chuẩn.
Daniel Katz

8

Đối với C ++ 11 , đây là một trình bao bọc đơn giản:

#include <iostream>
#include <chrono>

class Timer
{
public:
    Timer() : beg_(clock_::now()) {}
    void reset() { beg_ = clock_::now(); }
    double elapsed() const {
        return std::chrono::duration_cast<second_>
            (clock_::now() - beg_).count(); }

private:
    typedef std::chrono::high_resolution_clock clock_;
    typedef std::chrono::duration<double, std::ratio<1> > second_;
    std::chrono::time_point<clock_> beg_;
};

Hoặc đối với C ++ 03 trên * nix,

class Timer
{
public:
    Timer() { clock_gettime(CLOCK_REALTIME, &beg_); }

    double elapsed() {
        clock_gettime(CLOCK_REALTIME, &end_);
        return end_.tv_sec - beg_.tv_sec +
            (end_.tv_nsec - beg_.tv_nsec) / 1000000000.;
    }

    void reset() { clock_gettime(CLOCK_REALTIME, &beg_); }

private:
    timespec beg_, end_;
};

Ví dụ về cách sử dụng:

int main()
{
    Timer tmr;
    double t = tmr.elapsed();
    std::cout << t << std::endl;

    tmr.reset();
    t = tmr.elapsed();
    std::cout << t << std::endl;
    return 0;
}

Từ https://gist.github.com/gongzhitaao/7062087


5

Nói chung, để xác định thời gian mất bao lâu để gọi một hàm, bạn muốn thực hiện nó nhiều lần hơn chỉ một lần. Nếu bạn chỉ gọi chức năng của mình một lần và mất một thời gian rất ngắn để chạy, bạn vẫn phải thực sự gọi các chức năng hẹn giờ và bạn không biết điều đó mất bao lâu.

Ví dụ: nếu bạn ước tính hàm của mình có thể mất 800 ns để chạy, hãy gọi nó trong một vòng lặp mười triệu lần (sau đó sẽ mất khoảng 8 giây). Chia tổng thời gian cho mười triệu để có thời gian cho mỗi cuộc gọi.


Thực tế, tôi đang cố gắng đạt được hiệu suất của api cho một cuộc gọi cụ thể. đối với mỗi lần chạy, nó có thể cho một thời gian khác nhau, điều này có thể ảnh hưởng đến biểu đồ mà tôi thực hiện để cải thiện hiệu suất ... do đó thời gian tính bằng nano giây. nhưng vâng, đây là một ý tưởng tuyệt vời, sẽ xem xét nó.
gagneet

5

Bạn có thể sử dụng chức năng sau với gcc chạy trong bộ xử lý x86:

unsigned long long rdtsc()
{
  #define rdtsc(low, high) \
         __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))

  unsigned int low, high;
  rdtsc(low, high);
  return ((ulonglong)high << 32) | low;
}

với Digital Mars C ++:

unsigned long long rdtsc()
{
   _asm
   {
        rdtsc
   }
}

mà đọc bộ đếm thời gian hiệu suất cao trên chip. Tôi sử dụng điều này khi làm hồ sơ.


2
đây là hữu ích, tôi sẽ kiểm tra nếu bộ xử lý x86 là, như tôi đang sử dụng một mac táo để làm thí điểm ... nhờ :-)
gagneet

1
Những giá trị nào mà người dùng phải cung cấp cho giá trị cao và thấp? Tại sao bạn định nghĩa một macro bên trong phần thân của một hàm? Ngoài ra, dài, có lẽ được đánh máy thành dài không dấu, không phải là một kiểu tiêu chuẩn. Tôi muốn sử dụng điều này, nhưng tôi không chắc chắn như thế nào;)
Joseph Garvin

1
unsigned long không phải là điều phù hợp để sử dụng trong linux. Bạn có thể cân nhắc sử dụng int thay thế vì long và long đều là 64-bit trên Linux 64-bit.
Marius

3
Bộ đếm TSC ngày nay thường không đáng tin cậy: nó thay đổi tốc độ trên nhiều bộ xử lý khi tần số bị thay đổi và không nhất quán giữa các lõi khác nhau, do đó TSC không phải lúc nào cũng phát triển.
Blaisorblade

1
@Marius: Tôi đã triển khai nhận xét của bạn, sử dụng unsigned intlàm loại nội bộ.
Blaisorblade,

3

Nếu bạn cần độ chính xác đến từng giây, bạn cần sử dụng các phần mở rộng dành riêng cho hệ thống và sẽ phải kiểm tra với tài liệu dành cho hệ điều hành. POSIX hỗ trợ lên đến micro giây với gettimeofday , nhưng không có gì chính xác hơn vì máy tính không có tần số trên 1GHz.

Nếu bạn đang sử dụng Boost, bạn có thể kiểm tra boost :: posix_time .


muốn giữ mã di động, sẽ thấy thư viện tăng cường và kiểm tra xem tôi có thể gói phần mềm này với mã hay không. cảm ơn :-)
gagneet

3

Tôi đang sử dụng mã Borland đây là mã ti_hund cung cấp cho tôi một số lần số tiêu cực nhưng thời gian khá tốt.

#include <dos.h>

void main() 
{
struct  time t;
int Hour,Min,Sec,Hun;
gettime(&t);
Hour=t.ti_hour;
Min=t.ti_min;
Sec=t.ti_sec;
Hun=t.ti_hund;
printf("Start time is: %2d:%02d:%02d.%02d\n",
   t.ti_hour, t.ti_min, t.ti_sec, t.ti_hund);
....
your code to time
...

// read the time here remove Hours and min if the time is in sec

gettime(&t);
printf("\nTid Hour:%d Min:%d Sec:%d  Hundreds:%d\n",t.ti_hour-Hour,
                             t.ti_min-Min,t.ti_sec-Sec,t.ti_hund-Hun);
printf("\n\nAlt Ferdig Press a Key\n\n");
getch();
} // end main

3

Sử dụng phương pháp của Brock Adams, với một lớp đơn giản:

int get_cpu_ticks()
{
    LARGE_INTEGER ticks;
    QueryPerformanceFrequency(&ticks);
    return ticks.LowPart;
}

__int64 get_cpu_clocks()
{
    struct { int32 low, high; } counter;

    __asm cpuid
    __asm push EDX
    __asm rdtsc
    __asm mov counter.low, EAX
    __asm mov counter.high, EDX
    __asm pop EDX
    __asm pop EAX

    return *(__int64 *)(&counter);
}

class cbench
{
public:
    cbench(const char *desc_in) 
         : desc(strdup(desc_in)), start(get_cpu_clocks()) { }
    ~cbench()
    {
        printf("%s took: %.4f ms\n", desc, (float)(get_cpu_clocks()-start)/get_cpu_ticks());
        if(desc) free(desc);
    }
private:
    char *desc;
    __int64 start;
};

Ví dụ sử dụng:

int main()
{
    {
        cbench c("test");
        ... code ...
    }
    return 0;
}

Kết quả:

kiểm tra đã thực hiện: 0,0002 ms

Có một số chức năng gọi chi phí, nhưng vẫn phải đủ nhanh :)


3

Bạn có thể sử dụng Embedded Profiler (miễn phí cho Windows và Linux) có giao diện với bộ đếm thời gian đa nền (tính theo chu kỳ của bộ xử lý) và có thể cung cấp cho bạn số chu kỳ mỗi giây:

EProfilerTimer timer;
timer.Start();

... // Your code here

const uint64_t number_of_elapsed_cycles = timer.Stop();
const uint64_t nano_seconds_elapsed =
    mumber_of_elapsed_cycles / (double) timer.GetCyclesPerSecond() * 1000000000;

Việc tính toán lại số chu kỳ theo thời gian có thể là một thao tác nguy hiểm với các bộ xử lý hiện đại khi tần số CPU có thể được thay đổi động. Do đó, để chắc chắn rằng thời gian chuyển đổi là chính xác, cần phải sửa tần số bộ xử lý trước khi định cấu hình.


2

Nếu đây là dành cho Linux, tôi đã sử dụng hàm "gettimeofday", hàm này trả về một cấu trúc cung cấp giây và micro giây kể từ Kỷ nguyên. Sau đó, bạn có thể sử dụng bộ đếm thời gian để trừ hai để có được sự khác biệt về thời gian và chuyển nó thành bất kỳ độ chính xác nào của thời gian bạn muốn. Tuy nhiên, bạn chỉ định nano giây và có vẻ như hàm clock_gettime () là thứ bạn đang tìm kiếm. Nó đặt thời gian theo giây và nano giây vào cấu trúc mà bạn truyền vào nó.


clock_gettime () nên thực hiện thủ thuật ngay bây giờ. sẽ thử sử dụng cùng một mục đích của tôi ...
gagneet

2

Bạn nghĩ gì về điều này:

    int iceu_system_GetTimeNow(long long int *res)
    {
      static struct timespec buffer;
      // 
    #ifdef __CYGWIN__
      if (clock_gettime(CLOCK_REALTIME, &buffer))
        return 1;
    #else
      if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &buffer))
        return 1;
    #endif
      *res=(long long int)buffer.tv_sec * 1000000000LL + (long long int)buffer.tv_nsec;
      return 0;
    }

2

Đây là một bộ đếm thời gian Boost tuyệt vời hoạt động tốt:

//Stopwatch.hpp

#ifndef STOPWATCH_HPP
#define STOPWATCH_HPP

//Boost
#include <boost/chrono.hpp>
//Std
#include <cstdint>

class Stopwatch
{
public:
    Stopwatch();
    virtual         ~Stopwatch();
    void            Restart();
    std::uint64_t   Get_elapsed_ns();
    std::uint64_t   Get_elapsed_us();
    std::uint64_t   Get_elapsed_ms();
    std::uint64_t   Get_elapsed_s();
private:
    boost::chrono::high_resolution_clock::time_point _start_time;
};

#endif // STOPWATCH_HPP


//Stopwatch.cpp

#include "Stopwatch.hpp"

Stopwatch::Stopwatch():
    _start_time(boost::chrono::high_resolution_clock::now()) {}

Stopwatch::~Stopwatch() {}

void Stopwatch::Restart()
{
    _start_time = boost::chrono::high_resolution_clock::now();
}

std::uint64_t Stopwatch::Get_elapsed_ns()
{
    boost::chrono::nanoseconds nano_s = boost::chrono::duration_cast<boost::chrono::nanoseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(nano_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_us()
{
    boost::chrono::microseconds micro_s = boost::chrono::duration_cast<boost::chrono::microseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(micro_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_ms()
{
    boost::chrono::milliseconds milli_s = boost::chrono::duration_cast<boost::chrono::milliseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(milli_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_s()
{
    boost::chrono::seconds sec = boost::chrono::duration_cast<boost::chrono::seconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(sec.count());
}

2

Sao chép & dán-cấu trúc tối giản + sử dụng lười biếng

Nếu ý tưởng là có một cấu trúc tối giản mà bạn có thể sử dụng cho các bài kiểm tra nhanh, thì tôi khuyên bạn chỉ cần sao chép và dán vào bất kỳ đâu trong tệp C ++ của bạn ngay sau#include 's. Đây là trường hợp duy nhất mà tôi hy sinh định dạng kiểu Allman.

Bạn có thể dễ dàng điều chỉnh độ chính xác trong dòng đầu tiên của cấu trúc. Giá trị có thể là: nanoseconds, microseconds, milliseconds, seconds, minutes, hoặc hours.

#include <chrono>
struct MeasureTime
{
    using precision = std::chrono::microseconds;
    std::vector<std::chrono::steady_clock::time_point> times;
    std::chrono::steady_clock::time_point oneLast;
    void p() {
        std::cout << "Mark " 
                << times.size()/2
                << ": " 
                << std::chrono::duration_cast<precision>(times.back() - oneLast).count() 
                << std::endl;
    }
    void m() {
        oneLast = times.back();
        times.push_back(std::chrono::steady_clock::now());
    }
    void t() {
        m();
        p();
        m();
    }
    MeasureTime() {
        times.push_back(std::chrono::steady_clock::now());
    }
};

Sử dụng

MeasureTime m; // first time is already in memory
doFnc1();
m.t(); // Mark 1: next time, and print difference with previous mark
doFnc2();
m.t(); // Mark 2: next time, and print difference with previous mark
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.t(); // prints 'Mark 3: 123123' etc...

Kết quả đầu ra tiêu chuẩn

Mark 1: 123
Mark 2: 32
Mark 3: 433234

Nếu bạn muốn tóm tắt sau khi thực hiện

Nếu bạn muốn báo cáo sau đó, vì ví dụ mã của bạn ở giữa cũng ghi vào đầu ra chuẩn. Sau đó, thêm hàm sau vào struct (ngay trước MeasureTime ()):

void s() { // summary
    int i = 0;
    std::chrono::steady_clock::time_point tprev;
    for(auto tcur : times)
    {
        if(i > 0)
        {
            std::cout << "Mark " << i << ": "
                    << std::chrono::duration_cast<precision>(tprev - tcur).count()
                    << std::endl;
        }
        tprev = tcur;
        ++i;
    }
}

Vì vậy, bạn chỉ có thể sử dụng:

MeasureTime m;
doFnc1();
m.m();
doFnc2();
m.m();
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.m();
m.s();

Điều này sẽ liệt kê tất cả các dấu giống như trước đây, nhưng sau đó sau khi mã khác được thực thi. Lưu ý rằng bạn không nên sử dụng cả hai m.s()m.t().


Hoạt động hoàn hảo với OpenMP trên Ubuntu 16.04. Cảm ơn rất nhiều, đây sẽ là câu trả lời tốt nhất IMO!
Íhor Mé
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.