Funkcja timera zapewniająca czas w nano sekundach przy użyciu C ++

101

Chcę obliczyć czas, jaki zajęło API, aby zwrócić wartość. Czas potrzebny na takie działanie wynosi nanosekund. Ponieważ API jest klasą / funkcją C ++, używam timera.h, aby caculować to samo:

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

Powyższy kod podaje czas w sekundach. Jak uzyskać to samo w nanosekundach i z większą precyzją?

gagneet
źródło
powyższy kod oblicza się w sekundach, chcę uzyskać odpowiedź w nano sekundach ...
gagneet
Musisz dodać platformę do pytania (a najlepiej również do tytułu), aby uzyskać dobrą odpowiedź.
Patrick Johnmeyer
Oprócz znalezienia czasu, należy sprawdzić problemy z mikroznakowaniem (które jest niezwykle złożone) - wykonanie jednego wykonania i uzyskanie czasu na początku i na końcu raczej nie zapewni wystarczającej precyzji.
Blaisorblade
@Blaisorblade: Zwłaszcza, że ​​w niektórych moich testach odkryłem, że clock()nie jest tak szybko, jak myślałem.
Mooing Duck

Odpowiedzi:

83

To, co inni opublikowali na temat wielokrotnego uruchamiania funkcji w pętli, jest poprawne.

W przypadku Linuksa (i BSD) chcesz użyć 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
}

W przypadku okien chcesz użyć QueryPerformanceCounter . A tutaj jest więcej o QPC

Najwyraźniej istnieje znany problem z QPC na niektórych chipsetach, więc możesz chcieć się upewnić, że nie masz tych chipsetów. Ponadto niektóre dwurdzeniowe procesory AMD również mogą powodować problem . Zobacz drugi post sebbbi, w którym stwierdza:

QueryPerformanceCounter () i QueryPerformanceFrequency () oferują nieco lepszą rozdzielczość, ale mają inne problemy. Na przykład w systemie Windows XP wszystkie dwurdzeniowe procesory AMD Athlon X2 zwracają komputer z jednym z rdzeni „losowo” (komputer czasami przeskakuje nieco do tyłu), chyba że specjalnie zainstalujesz pakiet dwurdzeniowych sterowników AMD w celu rozwiązania problemu. Nie zauważyliśmy żadnych innych dwurdzeniowych procesorów z podobnymi problemami (p4 dual, p4 ht, core2 dual, core2 quad, phenom quad).

EDYCJA 2013/07/16:

Wygląda na to, że istnieją pewne kontrowersje dotyczące skuteczności QPC w pewnych okolicznościach, jak podano w http://msdn.microsoft.com/en-us/library/windows/desktop/ee417693(v=vs.85).aspx

... Podczas gdy QueryPerformanceCounter i QueryPerformanceFrequency zwykle dostosowują się do wielu procesorów, błędy w BIOS-ie lub sterownikach mogą powodować zwracanie przez te procedury różnych wartości, gdy wątek przechodzi z jednego procesora do drugiego ...

Jednak ta odpowiedź StackOverflow https://stackoverflow.com/a/4588605/34329 stwierdza, że ​​QPC powinno działać dobrze na każdym systemie operacyjnym MS OS po dodatku Service Pack 2 dla Win XP.

W tym artykule pokazano, że system Windows 7 może określić, czy procesor (y) mają niezmienny TSC i jeśli nie, powraca do zewnętrznego licznika czasu. http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html Synchronizacja między procesorami nadal stanowi problem.

Inne dokładne odczyty związane z licznikami czasu:

Więcej szczegółów w komentarzach.

smucić
źródło
1
Widziałem przesunięcie zegara TSC na starszym komputerze PC z dwoma procesorami Xeon, ale nie tak źle, jak na Athlonie X2 z włączoną funkcją narastania zegara C1. Przy narastaniu narastającym zegara C1, wykonanie instrukcji HLT spowalnia zegar, powodując wolniejszy przyrost TSC na rdzeniach bezczynnych niż na rdzeniach aktywnych.
bk1e
6
CLOCK_MONOTONIC działa na dostępnych wersjach Linuksa.
Bernard,
1
@Bernard - To musi być nowo dodane, odkąd ostatni raz na to patrzyłem. Dzięki za ostrzeżenie.
smuć się
3
W rzeczywistości musisz użyć CLOCK_MONOTONIC_RAW, jeśli jest dostępny, aby uzyskać czas sprzętowy nieskorygowany przez NTP.
Jak omówiono tutaj, poprawna implementacja QPC nie używa licznika TSC, przynajmniej tam, gdzie wiadomo, że jest zawodny: stackoverflow.com/q/510462/53974
Blaisorblade
69

Ta nowa odpowiedź wykorzystuje funkcję C ++ 11 <chrono>. Chociaż istnieją inne odpowiedzi, które pokazują, jak używać <chrono>, żadna z nich nie pokazuje, jak korzystać <chrono>z funkcji RDTSCwspomnianej w kilku innych odpowiedziach tutaj. Więc pomyślałem, że pokażę jak korzystać RDTSCz <chrono>. Dodatkowo będę pokazują, jak można templatize kod testowy na zegarze, dzięki czemu można szybko przełączać się między RDTSCa system wbudowany w obiektach zegar (który prawdopodobnie zostanie na podstawie clock(), clock_gettime()i / lub QueryPerformanceCounter.

Zwróć uwagę, że RDTSCinstrukcja jest specyficzna dla x86. QueryPerformanceCounterdotyczy tylko systemu Windows. I clock_gettime()jest tylko POSIX. Poniżej przedstawiam dwa nowe zegary: std::chrono::high_resolution_clocki std::chrono::system_clock, które, jeśli można założyć C ++ 11, są teraz wieloplatformowe.

Po pierwsze, oto jak utworzyć zegar zgodny z C ++ 11 na podstawie rdtscinstrukcji montażu Intela . Nazwę to 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

Wszystko, co robi ten zegar, to zliczanie cykli procesora i przechowywanie ich w 64-bitowej liczbie całkowitej bez znaku. Być może będziesz musiał dostosować składnię języka asemblera dla swojego kompilatora. Lub Twój kompilator może oferować funkcję wewnętrzną, której możesz użyć zamiast tego (np now() {return __rdtsc();}.).

Aby zbudować zegar, musisz nadać mu reprezentację (typ pamięci). Należy również podać okres zegara, który musi być stałą czasową kompilacji, nawet jeśli urządzenie może zmieniać częstotliwość zegara w różnych trybach zasilania. Na ich podstawie można łatwo zdefiniować „natywny” czas trwania i punkt czasowy swojego zegara, korzystając z tych podstaw.

Jeśli wszystko, co chcesz zrobić, to wyprowadzić liczbę taktów zegara, tak naprawdę nie ma znaczenia, jaką liczbę podasz dla okresu zegara. Ta stała pojawia się tylko wtedy, gdy chcesz przeliczyć liczbę tyknięć zegara na jakąś jednostkę czasu rzeczywistego, taką jak nanosekundy. W takim przypadku, im dokładniej jesteś w stanie podać częstotliwość zegara, tym dokładniejsza będzie konwersja na nanosekundy (milisekundy, cokolwiek).

Poniżej znajduje się przykładowy kod, który pokazuje, jak używać x::clock. Właściwie utworzyłem szablon kodu na zegarze, ponieważ chciałbym pokazać, jak można używać wielu różnych zegarów z dokładnie tą samą składnią. Ten konkretny test pokazuje, jakie jest obciążenie pętli podczas uruchamiania tego, co chcesz, w pętli:

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

Pierwszą rzeczą, jaką robi ten kod, jest utworzenie jednostki „czasu rzeczywistego” do wyświetlania wyników. Wybrałem pikosekundy, ale możesz wybrać dowolne jednostki, oparte na liczbach całkowitych lub zmiennoprzecinkowych. Jako przykład std::chrono::nanosecondsmogę użyć gotowej jednostki.

Jako inny przykład chcę wydrukować średnią liczbę cykli zegara na iterację jako zmiennoprzecinkową, więc tworzę inny czas trwania, oparty na liczbie podwójnej, który ma te same jednostki, co takt zegara (nazywany Cyclew kodzie).

Pętla jest synchronizowana z wywołaniami clock::now()po obu stronach. Jeśli chcesz nazwać typ zwracany przez tę funkcję, jest to:

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

(jak wyraźnie widać w x::clockprzykładzie, dotyczy to również zegarów dostarczanych przez system).

Aby uzyskać czas trwania w postaci taktów zegara zmiennoprzecinkowego, wystarczy odjąć dwa punkty czasowe, a aby uzyskać wartość na każdą iterację, podzielić ten czas trwania przez liczbę iteracji.

Możesz uzyskać liczbę w dowolnym czasie za pomocą count()funkcji członkowskiej. Zwraca wewnętrzną reprezentację. Na koniec używam, std::chrono::duration_castaby przekonwertować czas trwania Cyclena czas trwania picosecondsi wydrukować to.

Korzystanie z tego kodu jest proste:

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

Powyżej wykonuję test na naszej domowej roboty x::clocki porównuję te wyniki z dwoma zegarami dostarczonymi przez system: std::chrono::high_resolution_clocki std::chrono::system_clock. Dla mnie to drukuje:

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

Pokazuje to, że każdy z tych zegarów ma inny okres taktowania, ponieważ tiki na iterację są bardzo różne dla każdego zegara. Jednak po przeliczeniu na znaną jednostkę czasu (np. Pikosekundy), otrzymuję w przybliżeniu ten sam wynik dla każdego zegara (Twój przebieg może się różnić).

Zwróć uwagę, że mój kod jest całkowicie wolny od „magicznych stałych konwersji”. Rzeczywiście, w całym przykładzie są tylko dwie magiczne liczby:

  1. Szybkość zegara mojej maszyny w celu zdefiniowania x::clock.
  2. Liczba iteracji do przetestowania. Jeśli zmiana tej liczby powoduje znaczne różnice w wynikach, prawdopodobnie należy zwiększyć liczbę iteracji lub opróżnić komputer z konkurencyjnych procesów podczas testowania.
Howard Hinnant
źródło
5
Mówiąc „RDTSC to tylko Intel”, naprawdę masz na myśli architekturę x86 i pochodne, prawda? Układy AMD, Cyrix, Transmeta x86 mają instrukcje , a procesory Intel RISC i ARM nie.
Ben Voigt
1
@BenVoigt: +1 Tak, twoja poprawka jest całkiem poprawna, dziękuję.
Howard Hinnant
1
Jak wpłynie na to dławienie procesora? Czy częstotliwość zegara nie zmienia się w zależności od obciążenia procesora?
Tejas Kale
@TejasKale: Jest to opisane w odpowiedzi w dwóch kolejnych akapitach zaczynających się od „Aby zbudować zegar ...”. Zazwyczaj kod czasu nie mierzy pracy, która blokuje wątek (ale może). I tak zazwyczaj twój procesor nie będzie dławić. Ale jeśli mierzysz kod obejmujący sen, blokadę mutex, oczekiwanie zmienna warunkowa itp., rdtscZegar prawdopodobnie będzie miał niedokładne konwersje na inne jednostki. Warto tak ustawić swoje pomiary, aby można było łatwo zmieniać i porównywać zegary (jak pokazano w tej odpowiedzi).
Howard Hinnant
28

Przy takim poziomie dokładności lepiej byłoby rozumować taktowanie procesora, a nie wywołanie systemowe, takie jak clock () . I nie zapominaj, że jeśli wykonanie instrukcji zajmuje więcej niż jedną nanosekundę ... uzyskanie dokładności nanosekundowej jest prawie niemożliwe.

Jednak coś takiego to początek:

Oto rzeczywisty kod do pobierania liczby taktów zegara procesora 80x86, które minęły od ostatniego uruchomienia procesora. Będzie działać na Pentium i nowszych wersjach (386/486 nie są obsługiwane). Ten kod jest w rzeczywistości specyficzny dla MS Visual C ++, ale prawdopodobnie można go bardzo łatwo przenieść na cokolwiek innego, o ile obsługuje on asemblację wbudowaną.

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

}

Ta funkcja ma również tę zaletę, że jest niezwykle szybka - jej wykonanie zwykle nie zajmuje więcej niż 50 cykli procesora.

Korzystanie z danych
czasowych : Jeśli chcesz przeliczyć liczniki zegara na rzeczywisty upływający czas, podziel wyniki przez szybkość zegara chipa. Pamiętaj, że „oceniane” GHz może nieznacznie różnić się od rzeczywistej szybkości twojego układu. Aby sprawdzić prawdziwą prędkość swojego chipa, możesz użyć kilku bardzo dobrych narzędzi lub wywołania Win32, QueryPerformanceFrequency ().

VonC
źródło
dzięki za informację, to się przydaje. nie pomyślałem o cyklach procesora do obliczania czasu, myślę, że jest to bardzo dobry punkt, o którym należy pamiętać :-)
gagneet
4
Używanie QueryPerformanceFrequency () do przekształcania liczników TSC w upływający czas może nie działać. QueryPerformanceCounter () używa HPET (High Precision Event Timer) w systemie Vista, jeśli jest dostępny. Używa licznika czasu zarządzania energią ACPI, jeśli użytkownik doda / USEPMTIMER do boot.ini.
bk1e
23

Aby zrobić to poprawnie, możesz użyć jednego z dwóch sposobów, albo z, RDTSCalbo z clock_gettime(). Drugi jest około 2 razy szybszy i ma tę zaletę, że podaje właściwy czas absolutny. Zauważ, że RDTSCaby działał poprawnie, musisz go używać zgodnie ze wskazaniami (inne komentarze na tej stronie zawierają błędy i mogą dawać nieprawidłowe wartości taktowania na niektórych procesorach)

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

i dla clock_gettime: (arbitralnie wybrałem rozdzielczość mikrosekund)

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

czas i uzyskane wartości:

Absolute values:
rdtsc           = 4571567254267600
clock_gettime   = 1278605535506855

Processing time: (10000000 runs)
rdtsc           = 2292547353
clock_gettime   = 1031119636
Marius
źródło
22

Aby uzyskać pożądane rezultaty, używam:

#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;
}
gagneet
źródło
2
Głosowałem w dół, ponieważ próbując zastosować ten kod, musiałem najpierw wygooglować, dlaczego nie zdefiniowano przedziału czasu. Potem musiałem wygooglować co POSIX ... i tak jak to zrozumiałem, ten kod nie jest odpowiedni dla użytkowników Windows, którzy mają trzymać się standardowej biblioteki.
Daniel Katz
8

Dla C ++ 11 , oto prosta otoka:

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

Lub dla C ++ 03 na * 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_;
};

Przykład użycia:

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

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

gongzhitaao
źródło
5

Ogólnie rzecz biorąc, aby określić czas potrzebny do wywołania funkcji, chcesz to zrobić wielokrotnie więcej niż jeden raz. Jeśli wywołujesz swoją funkcję tylko raz, a jej uruchomienie zajmuje bardzo krótko, nadal masz narzut faktycznego wywoływania funkcji timera i nie wiesz, ile to trwa.

Na przykład, jeśli oszacujesz, że uruchomienie funkcji może zająć 800 ns, wywołaj ją w pętli dziesięć milionów razy (co zajmie wtedy około 8 sekund). Podziel całkowity czas przez dziesięć milionów, aby uzyskać czas na połączenie.

Greg Hewgill
źródło
w rzeczywistości próbuję uzyskać wydajność interfejsu API dla określonego wywołania. dla każdego przebiegu może dać inny czas, może to wpłynąć na wykres, który tworzę dla poprawy wydajności ... stąd czas w nano sekundach. ale tak, to świetny pomysł, rozważę to.
gagneet
5

Możesz użyć następującej funkcji z gcc działającym na procesorach 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;
}

z Digital Mars C ++:

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

który odczytuje licznik czasu o wysokiej wydajności w chipie. Używam tego podczas profilowania.

Walter Bright
źródło
2
to przydatne, sprawdzę, czy procesor to x86, ponieważ używam Apple Mac do eksperymentów ... dzięki :-)
gagneet
1
Jakie wartości ma przypisywać użytkownikowi wysokie i niskie? Dlaczego definiujesz makro w treści funkcji? Ponadto ulonglong, prawdopodobnie zdefiniowany jako unsigned long long, nie jest typem standardowym. Chciałbym to wykorzystać, ale nie jestem pewien jak;)
Joseph Garvin
1
unsigned long nie jest właściwym rozwiązaniem w Linuksie. Możesz rozważyć użycie int zamiast tego, ponieważ long i long long są 64-bitowe w 64-bitowym systemie Linux.
Marius,
3
Licznik TSC jest obecnie często zawodny: zmienia swoją prędkość na wielu procesorach, gdy zmienia się częstotliwość, i jest niespójny dla różnych rdzeni, dlatego TSC nie zawsze rośnie.
Blaisorblade
1
@Marius: Zaimplementowałem Twój komentarz, używając unsigned intjako typu wewnętrznego.
Blaisorblade
3

Jeśli potrzebujesz precyzji co do sekundy, musisz użyć rozszerzeń specyficznych dla systemu i sprawdzić w dokumentacji systemu operacyjnego. POSIX obsługuje do mikrosekund z gettimeofday , ale nic bardziej precyzyjnego, ponieważ komputery nie miały częstotliwości powyżej 1GHz.

Jeśli używasz Boost, możesz sprawdzić boost :: posix_time .

Raymond Martineau
źródło
chcesz zachować przenośność kodu, zobaczy bibliotekę boost i sprawdzi, czy mogę dołączyć ją do kodu. dzięki :-)
gagneet
3

Używam kodu Borlanda, tutaj kod ti_hund podaje mi czasami liczbę ujemną, ale czas jest dość dobry.

#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
Paul J Moesman
źródło
3

Korzystanie z metody Brocka Adamsa z prostą klasą:

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

Przykład użycia:

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

Wynik:

test trwał: 0,0002 ms

Ma narzut na wywołanie funkcji, ale powinien być wystarczająco szybki :)

Tomasz
źródło
3

Możesz użyć Embedded Profiler (darmowy dla Windows i Linux), który ma interfejs do wieloplatformowego timera (w liczbie cykli procesora) i może dać ci liczbę cykli na sekundę:

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;

Ponowne obliczanie liczby cykli w czasie jest prawdopodobnie niebezpieczną operacją w przypadku nowoczesnych procesorów, w których częstotliwość procesora można zmieniać dynamicznie. Dlatego, aby mieć pewność, że przekonwertowane czasy są poprawne, konieczne jest ustalenie częstotliwości procesora przed profilowaniem.

Mi-La
źródło
2

Jeśli to jest dla Linuksa, używam funkcji "gettimeofday", która zwraca strukturę, która podaje sekundy i mikrosekundy od Epoki. Następnie możesz użyć timersub, aby odjąć te dwie wartości, aby uzyskać różnicę w czasie, i przekonwertować ją na dowolną dokładność czasu. Jednak określasz nanosekundy i wygląda na to, że funkcja clock_gettime () jest tym, czego szukasz. Wprowadza czas w sekundach i nanosekundach do struktury, którą do niego przekazujesz.

Will Mc
źródło
Na razie clock_gettime () powinno załatwić sprawę. spróbuję użyć tego samego do moich celów ...
gagneet
2

Co myślicie o tym:

    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;
    }
icegood
źródło
2

Oto ładny timer doładowania, który działa dobrze:

//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());
}
Patrick K.
źródło
2

Minimalistyczna struktura kopiuj i wklej + leniwe użytkowanie

Jeśli chodzi o minimalistyczną strukturę, której możesz użyć do szybkich testów, sugeruję po prostu skopiować i wkleić w dowolnym miejscu w pliku C ++ zaraz po #include. To jedyny przypadek, w którym poświęcam formatowanie w stylu Allmana.

Możesz łatwo dostosować precyzję w pierwszym wierszu struktury. Możliwe wartości to: nanoseconds, microseconds, milliseconds, seconds, minutes, lub 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());
    }
};

Stosowanie

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

Standardowy wynik wyjściowy

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

Jeśli chcesz podsumować po wykonaniu

Jeśli chcesz otrzymać raport później, ponieważ na przykład Twój kod pomiędzy nimi również zapisuje na standardowe wyjście. Następnie dodaj następującą funkcję do struktury (tuż przed 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;
    }
}

Możesz więc po prostu użyć:

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

Który wyświetli wszystkie znaki, tak jak poprzednio, ale po wykonaniu drugiego kodu. Pamiętaj, że nie powinieneś używać obu m.s()i m.t().

Yeti
źródło
Doskonale współpracuje z OpenMP na Ubuntu 16.04. Wielkie dzięki, to powinno być najlepszą odpowiedzią IMO!
Íhor Mé