Fungsi Timer untuk menyediakan waktu dalam nano detik menggunakan C ++


101

Saya ingin menghitung waktu yang dibutuhkan API untuk mengembalikan nilai. Waktu yang dibutuhkan untuk tindakan semacam itu berada dalam rentang nano detik. Karena API adalah kelas / fungsi C ++, saya menggunakan timer.h untuk menghitung hal yang sama:

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

Kode di atas memberikan waktu dalam hitungan detik. Bagaimana cara mendapatkan yang sama dalam nano detik dan dengan lebih presisi?


kode di atas menghitung dalam hitungan detik, saya ingin mendapatkan jawabannya dalam nano detik ...
gagneet

Perlu menambahkan platform ke pertanyaan (dan lebih disukai ke judul juga) untuk mendapatkan jawaban yang bagus.
Patrick Johnmeyer

Selain mendapatkan waktu, seseorang perlu mencari masalah dengan microbenchmarking (yang sangat kompleks) - hanya melakukan satu eksekusi, dan mendapatkan waktu di awal dan akhir, sepertinya tidak akan memberikan cukup presisi.
Blaisorblade

@Blaisorblade: Terutama karena saya telah menemukan dalam beberapa pengujian saya yang clock()tidak secepat yang saya kira.
Mooing Duck

Jawaban:


83

Apa yang diposting orang lain tentang menjalankan fungsi berulang kali dalam satu putaran adalah benar.

Untuk Linux (dan BSD) Anda ingin menggunakan 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
}

Untuk windows Anda ingin menggunakan QueryPerformanceCounter . Dan ini lebih banyak tentang QPC

Tampaknya ada masalah yang diketahui dengan QPC pada beberapa chipset, jadi Anda mungkin ingin memastikan bahwa Anda tidak memiliki chipset tersebut. Selain itu, beberapa AMD inti ganda juga dapat menyebabkan masalah . Lihat posting kedua oleh sebbbi, di mana dia menyatakan:

QueryPerformanceCounter () dan QueryPerformanceFrequency () menawarkan resolusi yang sedikit lebih baik, tetapi memiliki masalah yang berbeda. Misalnya di Windows XP, semua CPU inti ganda AMD Athlon X2 mengembalikan PC dari salah satu inti "secara acak" (PC terkadang melompat sedikit ke belakang), kecuali jika Anda secara khusus menginstal paket driver inti ganda AMD untuk memperbaiki masalah. Kami belum melihat ada CPU dual + core lainnya yang mengalami masalah serupa (p4 dual, p4 ht, core2 dual, core2 quad, phenom quad).

EDIT 2013/07/16:

Sepertinya ada beberapa kontroversi tentang keefektifan QPC dalam keadaan tertentu seperti yang dinyatakan di http://msdn.microsoft.com/en-us/library/windows/desktop/ee417693(v=vs.85).aspx

... Meskipun QueryPerformanceCounter dan QueryPerformanceFrequency biasanya menyesuaikan untuk beberapa prosesor, bug di BIOS atau driver dapat mengakibatkan rutinitas ini mengembalikan nilai yang berbeda saat utas berpindah dari satu prosesor ke prosesor lainnya ...

Namun jawaban StackOverflow ini https://stackoverflow.com/a/4588605/34329 menyatakan bahwa QPC harus bekerja dengan baik di MS OS apa pun setelah paket layanan Win XP 2.

Artikel ini menunjukkan bahwa Windows 7 dapat menentukan apakah prosesor memiliki TSC invarian dan kembali ke pengatur waktu eksternal jika tidak. http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html Sinkronisasi antar prosesor masih menjadi masalah.

Bacaan bagus lainnya terkait pengatur waktu:

Lihat komentar untuk lebih jelasnya.


1
Saya telah melihat jam TSC condong pada PC Xeon ganda yang lebih tua, tetapi tidak seburuk pada Athlon X2 dengan jam C1 diaktifkan. Dengan clock C1 yang ramping, mengeksekusi instruksi HLT memperlambat clock, menyebabkan TSC pada core idle bertambah lebih lambat daripada pada core aktif.
bk1e

6
CLOCK_MONOTONIC berfungsi pada versi Linux yang saya miliki.
Bernard

1
@Bernard - Itu harus ditambahkan baru sejak saya terakhir melihat ini. Terimakasih atas peringatannya.
berduka

3
Bahkan, Anda harus menggunakan CLOCK_MONOTONIC_RAW, jika tersedia, agar waktu perangkat keras tidak disesuaikan dengan NTP.

Seperti yang dibahas di sini, implementasi QPC yang benar tidak menggunakan penghitung TSC, setidaknya di tempat yang diketahui tidak dapat diandalkan: stackoverflow.com/q/510462/53974
Blaisorblade

69

Jawaban baru ini menggunakan fasilitas C ++ 11 <chrono>. Meskipun ada jawaban lain yang menunjukkan cara menggunakan <chrono>, tidak satupun yang menunjukkan cara menggunakan <chrono>dengan RDTSCfasilitas yang disebutkan dalam beberapa jawaban lain di sini. Jadi saya pikir saya akan menunjukkan cara menggunakan RDTSCdengan <chrono>. Selain itu, saya akan mendemonstrasikan bagaimana Anda dapat membuat template kode pengujian pada jam sehingga Anda dapat dengan cepat beralih di antara RDTSCdan fasilitas jam bawaan sistem Anda (yang kemungkinan akan didasarkan pada clock(), clock_gettime()dan / atau QueryPerformanceCounter.

Perhatikan bahwa RDTSCinstruksinya khusus x86. QueryPerformanceCounterhanya untuk Windows. Dan clock_gettime()hanya POSIX. Di bawah ini saya perkenalkan dua jam baru: std::chrono::high_resolution_clockdan std::chrono::system_clock, yang, jika Anda dapat mengasumsikan C ++ 11, sekarang bersifat lintas platform.

Pertama, berikut adalah cara Anda membuat jam yang kompatibel dengan C ++ 11 dari rdtscinstruksi perakitan Intel . Saya akan menyebutnya 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

Semua jam ini menghitung siklus CPU dan menyimpannya dalam integer 64-bit unsigned. Anda mungkin perlu mengubah sintaks bahasa assembly untuk kompiler Anda. Atau kompilator Anda mungkin menawarkan intrinsik yang dapat Anda gunakan sebagai gantinya (mis now() {return __rdtsc();}.).

Untuk membuat jam, Anda harus memberikan representasi (tipe penyimpanan). Anda juga harus menyediakan periode jam, yang harus berupa konstanta waktu kompilasi, meskipun mesin Anda mungkin mengubah kecepatan jam dalam mode daya yang berbeda. Dan dari sana, Anda dapat dengan mudah menentukan durasi waktu "asli" jam dan titik waktu dalam kaitannya dengan dasar-dasar ini.

Jika yang ingin Anda lakukan hanyalah menampilkan jumlah detak jam, tidak masalah angka apa yang Anda berikan untuk periode jam tersebut. Konstanta ini hanya berperan jika Anda ingin mengubah jumlah jam menjadi beberapa unit waktu nyata seperti nanodetik. Dan dalam hal ini, semakin akurat Anda dapat memberikan kecepatan clock, semakin akurat konversi ke nanodetik, (milidetik, apa pun).

Di bawah ini adalah contoh kode yang menunjukkan cara menggunakan x::clock. Sebenarnya saya telah membuat template kode pada jam karena saya ingin menunjukkan bagaimana Anda dapat menggunakan banyak jam berbeda dengan sintaks yang sama persis. Tes khusus ini menunjukkan apa overhead perulangan saat menjalankan apa yang Anda inginkan untuk waktu di bawah satu loop:

#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";
}

Hal pertama yang dilakukan kode ini adalah membuat unit "waktu nyata" untuk menampilkan hasilnya. Saya telah memilih picoseconds, tetapi Anda dapat memilih unit apa pun yang Anda suka, baik integral atau berbasis floating point. Sebagai contoh ada std::chrono::nanosecondsunit yang sudah jadi yang bisa saya gunakan.

Sebagai contoh lain, saya ingin mencetak jumlah rata-rata siklus jam per iterasi sebagai floating point, jadi saya membuat durasi lain, berdasarkan ganda, yang memiliki unit yang sama dengan tick jam (disebut Cycledalam kode).

Loop diatur dengan panggilan ke clock::now()salah satu sisi. Jika Anda ingin memberi nama tipe yang dikembalikan dari fungsi ini:

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

(seperti yang ditunjukkan dengan jelas dalam x::clockcontoh, dan juga berlaku untuk jam yang dipasok sistem).

Untuk mendapatkan durasi dalam hal jam floating point, seseorang hanya mengurangi dua titik waktu, dan untuk mendapatkan nilai per iterasi, bagi durasi itu dengan jumlah iterasi.

Anda bisa mendapatkan hitungan dalam durasi berapa pun dengan menggunakan count()fungsi anggota. Ini mengembalikan representasi internal. Akhirnya saya gunakan std::chrono::duration_castuntuk mengubah durasi Cyclemenjadi durasi picosecondsdan mencetaknya.

Untuk menggunakan kode ini sederhana:

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>();
}

Di atas saya melakukan pengujian menggunakan buatan kami x::clock, dan membandingkan hasil tersebut dengan menggunakan dua jam yang dipasok sistem: std::chrono::high_resolution_clockdan std::chrono::system_clock. Bagi saya ini cetakannya:

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

Ini menunjukkan bahwa setiap jam ini memiliki periode detak yang berbeda, karena detak per iterasi sangat berbeda untuk setiap jam. Namun, ketika dikonversi ke satuan waktu yang diketahui (misalnya pikodetik), saya mendapatkan hasil yang kira-kira sama untuk setiap jam (jarak tempuh Anda mungkin berbeda-beda).

Perhatikan bagaimana kode saya benar-benar bebas dari "konstanta konversi ajaib". Memang, hanya ada dua angka ajaib di seluruh contoh:

  1. Kecepatan clock mesin saya untuk ditentukan x::clock.
  2. Jumlah iterasi yang akan diuji. Jika mengubah nomor ini membuat hasil Anda sangat bervariasi, maka Anda mungkin harus membuat jumlah iterasi lebih tinggi, atau mengosongkan komputer Anda dari proses yang bersaing saat pengujian.

5
Dengan "RDTSC is Intel-only", Anda benar-benar mengacu pada arsitektur dan turunan x86, bukan? Chip AMD, Cyrix, Transmeta x86 memiliki instruksi , dan prosesor Intel RISC dan ARM tidak.
Ben Voigt

1
@BenVoigt: +1 Ya, koreksi Anda cukup benar, terima kasih.
Howard Hinnant

1
Bagaimana pelambatan CPU akan mempengaruhi ini? Bukankah kecepatan clock berubah berdasarkan beban cpu?
Tejas Kale

@TejasKale: Ini dijelaskan dalam jawaban dalam dua paragraf berturut-turut yang dimulai dengan "Untuk membangun jam Anda ...". Biasanya kode waktu tidak mengukur pekerjaan yang memblokir utas (tetapi bisa). Dan biasanya CPU Anda tidak akan terhambat. Tetapi jika Anda mengukur kode yang melibatkan sleep, mutex lock, condition_variable wait, dll, rdtscjam kemungkinan memiliki konversi yang tidak akurat ke unit lain. Sebaiknya setel pengukuran Anda sehingga Anda dapat dengan mudah mengubah dan membandingkan jam (seperti yang ditunjukkan dalam jawaban ini).
Howard Hinnant

27

Dengan tingkat akurasi tersebut, akan lebih baik untuk bernalar di centang CPU daripada di panggilan sistem seperti clock () . Dan jangan lupa bahwa jika diperlukan lebih dari satu nanodetik untuk menjalankan instruksi ... memiliki akurasi nanodetik hampir tidak mungkin.

Namun, hal seperti itu adalah permulaan:

Berikut adalah kode sebenarnya untuk mengambil nomor dari jam CPU 80x86 yang berlalu sejak CPU terakhir kali dijalankan. Ini akan bekerja pada Pentium dan di atasnya (386/486 tidak didukung). Kode ini sebenarnya spesifik untuk MS Visual C ++, tetapi mungkin dapat dengan mudah di-porting ke yang lain, selama mendukung perakitan inline.

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);

}

Fungsi ini juga memiliki keuntungan karena sangat cepat - biasanya tidak lebih dari 50 siklus cpu untuk dijalankan.

Menggunakan Angka Waktu :
Jika Anda perlu menerjemahkan hitungan jam menjadi waktu berlalu yang sebenarnya, bagi hasil dengan kecepatan jam chip Anda. Ingatlah bahwa "terukur" GHz kemungkinan besar akan sedikit berbeda dari kecepatan sebenarnya dari chip Anda. Untuk memeriksa kecepatan sebenarnya dari chip Anda, Anda dapat menggunakan beberapa utilitas yang sangat bagus atau panggilan Win32, QueryPerformanceFrequency ().


terima kasih atas informasinya, ini bermanfaat. saya tidak memikirkan siklus cpu untuk menghitung waktu, saya pikir itu adalah hal yang sangat baik untuk diingat :-)
gagneet

4
Menggunakan QueryPerformanceFrequency () untuk mengubah jumlah TSC menjadi waktu yang berlalu mungkin tidak berfungsi. QueryPerformanceCounter () menggunakan HPET (High Precision Event Timer) di Vista jika tersedia. Ini menggunakan pengatur waktu manajemen daya ACPI jika pengguna menambahkan / USEPMTIMER ke boot.ini.
bk1e

23

Untuk melakukan ini dengan benar, Anda dapat menggunakan salah satu dari dua cara, pergi dengan RDTSCatau dengan clock_gettime(). Yang kedua kira-kira 2 kali lebih cepat dan memiliki keuntungan memberikan waktu absolut yang tepat. Perhatikan bahwa RDTSCagar berfungsi dengan benar Anda perlu menggunakannya seperti yang ditunjukkan (komentar lain di halaman ini memiliki kesalahan, dan mungkin menghasilkan nilai waktu yang salah pada prosesor tertentu)

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

dan untuk clock_gettime: (Saya memilih resolusi mikrodetik secara sewenang-wenang)

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

waktu dan nilai yang dihasilkan:

Absolute values:
rdtsc           = 4571567254267600
clock_gettime   = 1278605535506855

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

22

Saya menggunakan yang berikut ini untuk mendapatkan hasil yang diinginkan:

#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
Saya downvoted karena mencoba menerapkan kode ini saya harus terlebih dahulu ke google mengapa timespec tidak ditentukan. Lalu saya harus google whats POSIX ... dan seperti yang saya mengerti, kode ini tidak relevan untuk pengguna Windows yang harus tetap menggunakan perpustakaan standar.
Daniel Katz

8

Untuk C ++ 11 , berikut ini pembungkus sederhana:

#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_;
};

Atau untuk C ++ 03 di * 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_;
};

Contoh penggunaan:

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

Dari https://gist.github.com/gongzhitaao/7062087


5

Secara umum, untuk menentukan waktu berapa lama untuk memanggil suatu fungsi, Anda ingin melakukannya lebih dari sekali. Jika Anda memanggil fungsi Anda hanya sekali dan itu membutuhkan waktu yang sangat singkat untuk dijalankan, Anda masih memiliki overhead untuk benar-benar memanggil fungsi pengatur waktu dan Anda tidak tahu berapa lama waktu yang dibutuhkan.

Misalnya, jika Anda memperkirakan fungsi Anda mungkin membutuhkan 800 ns untuk dijalankan, panggil dalam loop sepuluh juta kali (yang kemudian akan memakan waktu sekitar 8 detik). Bagilah total waktu dengan sepuluh juta untuk mendapatkan waktu setiap panggilan.


sebenarnya, saya mencoba untuk mendapatkan kinerja api untuk panggilan tertentu. untuk setiap run mungkin memberikan waktu yang berbeda, ini dapat mempengaruhi grafik yang saya buat untuk peningkatan kinerja ... maka waktu dalam nano detik. tapi ya, ini adalah ide yang bagus, akan saya pertimbangkan.
gagneet

5

Anda dapat menggunakan fungsi berikut dengan gcc yang berjalan di bawah prosesor 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;
}

dengan Digital Mars C ++:

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

yang membaca pengatur waktu kinerja tinggi pada chip. Saya menggunakan ini saat melakukan pembuatan profil.


2
ini berguna, saya akan memeriksa apakah prosesornya x86, karena saya menggunakan mac apel untuk eksperimen ... terima kasih :-)
gagneet

1
Nilai apa yang seharusnya diberikan pengguna untuk tinggi dan rendah? Mengapa Anda menentukan makro di dalam badan fungsi? Juga, ulonglong, mungkin typedef'd hingga unsigned long long, bukanlah tipe standar. Saya ingin menggunakan ini tetapi saya tidak yakin bagaimana caranya;)
Joseph Garvin

1
unsigned long bukanlah hal yang tepat untuk digunakan di linux. Anda mungkin ingin mempertimbangkan untuk menggunakan int selama dan selama keduanya 64-bit di Linux 64-bit.
Marius

3
Penghitung TSC saat ini seringkali tidak dapat diandalkan: ia mengubah kecepatannya pada banyak prosesor ketika frekuensi diubah, dan tidak konsisten di berbagai inti, oleh karena itu TSC tidak selalu tumbuh.
Blaisorblade

1
@Marius: Saya menerapkan komentar Anda, menggunakan unsigned intsebagai tipe internal.
Blaisorblade

3

Jika Anda memerlukan ketepatan sub-detik, Anda perlu menggunakan ekstensi khusus sistem, dan harus memeriksa dengan dokumentasi untuk sistem operasi tersebut. POSIX mendukung hingga mikrodetik dengan gettimeofday , tetapi tidak ada yang lebih presisi karena komputer tidak memiliki frekuensi di atas 1GHz.

Jika Anda menggunakan Boost, Anda dapat memeriksa boost :: posix_time .


ingin menyimpan kode portabel, akan melihat pustaka peningkatan dan memeriksa apakah saya dapat menggabungkannya dengan kode. terima kasih :-)
gagneet

3

Saya menggunakan kode Borland di sini adalah kode ti_hund memberi saya beberapa kali angka negatif tetapi waktunya cukup baik.

#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

Menggunakan metode Brock Adams, dengan kelas sederhana:

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

Contoh Penggunaan:

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

Hasil:

Tes memakan waktu: 0,0002 ms

Memiliki beberapa overhead panggilan fungsi, tetapi masih harus lebih dari cukup cepat :)


3

Anda dapat menggunakan Embedded Profiler (gratis untuk Windows dan Linux) yang memiliki antarmuka ke pengatur waktu multiplatform (dalam hitungan siklus prosesor) dan dapat memberi Anda sejumlah siklus per detik:

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;

Penghitungan ulang hitungan siklus ke waktu mungkin merupakan operasi berbahaya dengan prosesor modern di mana frekuensi CPU dapat diubah secara dinamis. Oleh karena itu untuk memastikan bahwa waktu yang dikonversi benar, frekuensi prosesor harus diperbaiki sebelum membuat profil.


2

Jika ini untuk Linux, saya telah menggunakan fungsi "gettimeofday", yang mengembalikan struct yang memberikan detik dan mikrodetik sejak Epoch. Anda kemudian dapat menggunakan sub waktu untuk mengurangi keduanya untuk mendapatkan perbedaan waktu, dan mengubahnya menjadi ketepatan waktu yang Anda inginkan. Namun, Anda menentukan nanodetik, dan sepertinya fungsi clock_gettime () adalah yang Anda cari. Ini menempatkan waktu dalam hitungan detik dan nanodetik ke dalam struktur yang Anda lewati.


clock_gettime () harus melakukan trik untuk saat ini. akan mencoba menggunakan hal yang sama untuk tujuan saya ...
gagneet

2

Apa pendapatmu tentang itu:

    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

Berikut adalah pengatur waktu Boost yang berfungsi dengan baik:

//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

Salin & tempel-struct minimalis + penggunaan malas

Jika idenya adalah memiliki struct minimalis yang dapat Anda gunakan untuk tes cepat, maka saya sarankan Anda cukup salin dan tempelkan di mana saja di file C ++ Anda tepat setelah #includeitu. Ini adalah satu-satunya contoh di mana saya mengorbankan pemformatan gaya Allman.

Anda dapat dengan mudah menyesuaikan presisi di baris pertama struct. Nilai yang mungkin adalah: nanoseconds, microseconds, milliseconds, seconds, minutes, atau 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());
    }
};

Pemakaian

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

Hasil keluaran standar

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

Jika Anda ingin ringkasan setelah eksekusi

Jika Anda menginginkan laporan setelahnya, karena misalnya kode Anda di antaranya juga menulis ke output standar. Kemudian tambahkan fungsi berikut ke struct (tepat sebelum 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;
    }
}

Jadi Anda bisa menggunakan:

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

Yang akan mencantumkan semua tanda seperti sebelumnya, tetapi kemudian setelah kode lain dijalankan. Perhatikan bahwa Anda tidak boleh menggunakan keduanya m.s()dan m.t().


Bekerja sempurna dengan OpenMP di Ubuntu 16.04. Terima kasih banyak, ini adalah jawaban terbaik IMO!
Íhor Mé
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.