Jak obliczyć czas wykonania fragmentu kodu w C ++


121

Muszę obliczyć czas wykonania fragmentu kodu C ++ w sekundach. Musi działać na komputerach z systemem Windows lub Unix.

Używam kodu następującego kodu, aby to zrobić. (zaimportuj przed)

clock_t startTime = clock();
// some code here
// to compute its execution duration in runtime
cout << double( clock() - startTime ) / (double)CLOCKS_PER_SEC<< " seconds." << endl;

Jednak w przypadku małych danych wejściowych lub krótkich instrukcji, takich jak a = a + 1, otrzymuję wynik „0 sekund”. Myślę, że to musi być około 0,0000001 sekundy lub coś w tym rodzaju.

Pamiętam, że System.nanoTime()w Javie działa w tym przypadku całkiem nieźle. Jednak nie mogę uzyskać tej samej dokładnej funkcjonalności z clock()funkcji C ++.

Masz rozwiązanie?


29
Należy pamiętać, że każde porównanie oparte na różnicy czasu może być niedokładne ze względu na fakt, że system operacyjny może nie uruchamiać wątku od początku do końca. Może to przerwać i uruchomić inne wątki przeplatane z twoim, co będzie miało znaczący wpływ na rzeczywisty czas potrzebny do zakończenia operacji. Możesz uruchomić wiele razy i uśrednić wyniki; możesz zminimalizować liczbę innych uruchomionych procesów. Ale żaden z nich nie wyeliminuje całkowicie efektu zawieszenia nici.
Mordachai

14
Mordachi, dlaczego chcesz go wyeliminować? Chcesz zobaczyć, jak działa twoja funkcja w prawdziwym świecie, a nie w magicznej krainie, w której wątki nigdy nie są przerywane. Tak długo, jak uruchomisz go kilka razy i obliczysz średnią, będzie bardzo dokładny.
Thomas Bonini,

Tak, uruchomiłem to kilka razy i uzyskałem średnie wyniki.
AhmetB - Google

14
Andreas, komentarz Mordachaja ma znaczenie, jeśli OP chciałby porównać wydajność swojego kodu z innym algorytmem. Na przykład, jeśli przeprowadzi kilka testów zegara dziś po południu, a jutro rano przetestuje inny algorytm, jego porównanie może nie być wiarygodne, ponieważ może dzielić zasoby z większą liczbą procesów po południu niż rano. A może jeden zestaw kodu spowoduje, że system operacyjny skróci czas przetwarzania. Istnieje wiele powodów, dla których ten rodzaj pomiaru wydajności jest niewiarygodny, jeśli chce on przeprowadzić porównanie oparte na czasie.
weberc2

4
@Mordachai Wiem, że odpowiadam na stary komentarz, ale dla każdego, kto natknie się na to tak jak ja - od czasu do czasu wydajność algorytmów chcesz wykonać minimum kilka przebiegów, a nie średnią. To jest ten, który miał najmniej zakłóceń w systemie operacyjnym, podobnie jak synchronizacja głównie kodu.
Baruch

Odpowiedzi:


115

Możesz użyć tej funkcji, którą napisałem. Wołasz GetTimeMs64()i zwraca liczbę milisekund, które upłynęły od epoki unixowej przy użyciu zegara systemowego - podobnie time(NULL), z wyjątkiem milisekund.

Działa zarówno w systemie Windows, jak i Linux; jest bezpieczny dla wątków.

Zauważ, że szczegółowość wynosi 15 ms w systemie Windows; w Linuksie jest to zależne od implementacji, ale zwykle również 15 ms.

#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/time.h>
#include <ctime>
#endif

/* Remove if already defined */
typedef long long int64; typedef unsigned long long uint64;

/* Returns the amount of milliseconds elapsed since the UNIX epoch. Works on both
 * windows and linux. */

uint64 GetTimeMs64()
{
#ifdef _WIN32
 /* Windows */
 FILETIME ft;
 LARGE_INTEGER li;

 /* Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC) and copy it
  * to a LARGE_INTEGER structure. */
 GetSystemTimeAsFileTime(&ft);
 li.LowPart = ft.dwLowDateTime;
 li.HighPart = ft.dwHighDateTime;

 uint64 ret = li.QuadPart;
 ret -= 116444736000000000LL; /* Convert from file time to UNIX epoch time. */
 ret /= 10000; /* From 100 nano seconds (10^-7) to 1 millisecond (10^-3) intervals */

 return ret;
#else
 /* Linux */
 struct timeval tv;

 gettimeofday(&tv, NULL);

 uint64 ret = tv.tv_usec;
 /* Convert from micro seconds (10^-6) to milliseconds (10^-3) */
 ret /= 1000;

 /* Adds the seconds (10^0) after converting them to milliseconds (10^-3) */
 ret += (tv.tv_sec * 1000);

 return ret;
#endif
}

1
Na przyszłość: po prostu wrzucam go do pliku nagłówkowego i używam. Cieszę się, że to mam.
Daniel Handojo,

1
Uważam, że ta metoda gettimeofdaymoże dać niezamierzony wynik, jeśli zegar systemowy zostanie zmieniony. Jeśli byłby to dla Ciebie problem, możesz clock_gettimezamiast tego przyjrzeć się .
Azmisov

Czy ta metoda dla systemu Windows ma jakieś zalety GetTickCount?
MicroVirus

Nie kompiluje się przy użyciugcc -std=c99
Assimilater,

@MicroVirus: tak, GetTickCountto czas, który upłynął od uruchomienia systemu, podczas gdy moja funkcja zwraca czas od epoki UNIX, co oznacza, że ​​możesz jej używać do dat i godzin. Jeśli interesuje Cię tylko czas, który upłynął między dwoma zdarzeniami, mój jest nadal lepszym wyborem, ponieważ jest to int64; GetTickCount to int32 i przepełnia się co 50 dni, co oznacza, że ​​możesz uzyskać dziwne wyniki, jeśli dwa zarejestrowane zdarzenia znajdują się między przepełnieniem.
Thomas Bonini

43

Mam inny działający przykład, który używa mikrosekund (UNIX, POSIX itp.).

    #include <sys/time.h>
    typedef unsigned long long timestamp_t;

    static timestamp_t
    get_timestamp ()
    {
      struct timeval now;
      gettimeofday (&now, NULL);
      return  now.tv_usec + (timestamp_t)now.tv_sec * 1000000;
    }

    ...
    timestamp_t t0 = get_timestamp();
    // Process
    timestamp_t t1 = get_timestamp();

    double secs = (t1 - t0) / 1000000.0L;

Oto plik, w którym to zakodowaliśmy:

https://github.com/arhuaco/junkcode/blob/master/emqbit-bench/bench.c


5
Powinieneś dodać #include <sys/time.h>na początku swojego przykładu.
niekas

40

Oto proste rozwiązanie w C ++ 11, które daje satysfakcjonującą rozdzielczość.

#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 na * nix, dla c ++ 03

#include <iostream>
#include <ctime>

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

Oto 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


Otrzymuję ten błąd z twoim rozwiązaniem C ++ 11:/usr/lib/x86_64-linux-gnu/libstdc++.so.6: version GLIBCXX_3.4.19 not found (required by ../cpu_2d/g500)
user9869932

@julianromera z jakiej platformy korzystasz? czy zainstalowałeś bibliotekę libstdc ++ i g ++?
gongzhitaao

Jest to siatka Slurm systemu Linux Ubuntu 12. Właśnie to naprawiłem. Dodałem -static-libstdc ++ na końcu linkera. Dziękuję za pytanie @gongzhitaao
user9869932

18
#include <boost/progress.hpp>

using namespace boost;

int main (int argc, const char * argv[])
{
  progress_timer timer;

  // do stuff, preferably in a 100x loop to make it take longer.

  return 0;
}

Gdy progress_timerwyjdzie poza zakres, wydrukuje czas, który upłynął od jego utworzenia.

AKTUALIZACJA : Oto wersja, która działa bez funkcji Boost (testowana na macOS / iOS):

#include <chrono>
#include <string>
#include <iostream>
#include <math.h>
#include <unistd.h>

class NLTimerScoped {
private:
    const std::chrono::steady_clock::time_point start;
    const std::string name;

public:
    NLTimerScoped( const std::string & name ) : name( name ), start( std::chrono::steady_clock::now() ) {
    }


    ~NLTimerScoped() {
        const auto end(std::chrono::steady_clock::now());
        const auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>( end - start ).count();

        std::cout << name << " duration: " << duration_ms << "ms" << std::endl;
    }

};

int main(int argc, const char * argv[]) {

    {
        NLTimerScoped timer( "sin sum" );

        float a = 0.0f;

        for ( int i=0; i < 1000000; i++ ) {
            a += sin( (float) i / 100 );
        }

        std::cout << "sin sum = " << a << std::endl;
    }



    {
        NLTimerScoped timer( "sleep( 4 )" );

        sleep( 4 );
    }



    return 0;
}

2
To działa, ale pamiętaj, że progress_timer jest przestarzała (czasami przed boostem 1.50) - auto_cpu_timer może być bardziej odpowiednie.
davidA

3
@meowsqueak hmm, auto_cpu_timer wydaje się wymagać połączenia biblioteki systemu Boost, więc nie jest to już rozwiązanie tylko z nagłówkami. Szkoda ... nagle sprawia, że ​​inne opcje stają się bardziej atrakcyjne.
Tomas Andrle

1
tak, to dobra uwaga, jeśli jeszcze nie łączysz Boost, to jest więcej kłopotów niż warto. Ale jeśli już to robisz, działa całkiem nieźle.
davidA

@meowsqueak Tak, lub do szybkich testów porównawczych, po prostu pobierz starszą wersję Boost.
Tomas Andrle

@TomasAndrle Link już nie istnieje.
Zheng Qu

5

Windows udostępnia funkcję QueryPerformanceCounter (), a Unix - gettimeofday () Obie funkcje mogą mierzyć różnicę co najmniej 1 mikro-sekundy.


Ale używanie windows.h jest ograniczone. To samo skompilowane źródło musi działać zarówno w systemie Windows, jak i Unix. Jak sobie z tym poradzić?
AhmetB - Google

2
Następnie poszukaj biblioteki stackoverflow.com/questions/1487695/ ...
Captain Comic

4
to samo skompilowane źródło brzmi tak, jakbyś chciał uruchomić ten sam plik binarny w obu systemach, co nie wydaje się mieć miejsca. jeśli oznaczało to samo źródło wówczas #ifdefmusi być ok (i to sądząc po odpowiedzi zostały zaakceptowane), a następnie nie widzę problemu: #ifdef WIN32 #include <windows.h> ... #else ... #endif.
po prostu ktoś

3

W niektórych programach, które napisałem, używałem do tego celu RDTS . RDTSC nie dotyczy czasu, ale liczby cykli od uruchomienia procesora. Musisz skalibrować go w swoim systemie, aby uzyskać wynik w ciągu sekundy, ale jest to naprawdę przydatne, gdy chcesz ocenić wydajność, nawet lepiej jest użyć liczby cykli bezpośrednio, bez próby zmiany ich z powrotem na sekundy.

(powyższy link prowadzi do strony francuskiej Wikipedii, ale zawiera próbki kodu C ++, angielska wersja jest tutaj )


2

Proponuję wykorzystać standardowe funkcje biblioteczne do uzyskania informacji o czasie z systemu.

Jeśli chcesz uzyskać lepszą rozdzielczość, wykonaj więcej iteracji wykonania. Zamiast uruchamiać program raz i pobierać próbki, uruchom go 1000 lub więcej razy.


2

Lepiej jest uruchomić wewnętrzną pętlę kilka razy z taktowaniem wydajności tylko raz i średnio, dzieląc powtórzenia pętli wewnętrznej, niż uruchamiać całość (pętla + synchronizacja wydajności) kilka razy i średnio. Zmniejszy to obciążenie związane z kodem czasowym wydajności w porównaniu z rzeczywistą sekcją profilowaną.

Zawiń wywołania timera dla odpowiedniego systemu. W systemie Windows QueryPerformanceCounter jest dość szybki i „bezpieczny” w użyciu.

Możesz użyć "rdtsc" również na każdym nowoczesnym komputerze PC X86, ale mogą wystąpić problemy na niektórych maszynach wielordzeniowych (skakanie rdzenia może zmienić licznik czasu) lub jeśli masz włączony jakiś rodzaj szybkości.


2

(rozwiązanie specyficzne dla systemu Windows) Obecnie (około 2017 r.) sposobem uzyskania dokładnych czasów w systemie Windows jest użycie „QueryPerformanceCounter”. Takie podejście ma tę zaletę, że daje bardzo dokładne wyniki i jest zalecane przez MS. Po prostu umieść kod blob w nowej aplikacji konsoli, aby uzyskać działający przykład. Istnieje tu długa dyskusja: Pozyskiwanie znaczników czasu w wysokiej rozdzielczości

#include <iostream>
#include <tchar.h>
#include <windows.h>

int main()
{
constexpr int MAX_ITER{ 10000 };
constexpr __int64 us_per_hour{ 3600000000ull }; // 3.6e+09
constexpr __int64 us_per_min{ 60000000ull };
constexpr __int64 us_per_sec{ 1000000ull };
constexpr __int64 us_per_ms{ 1000ull };

// easy to work with
__int64 startTick, endTick, ticksPerSecond, totalTicks = 0ull;

QueryPerformanceFrequency((LARGE_INTEGER *)&ticksPerSecond);

for (int iter = 0; iter < MAX_ITER; ++iter) {// start looping
    QueryPerformanceCounter((LARGE_INTEGER *)&startTick); // Get start tick
    // code to be timed
    std::cout << "cur_tick = " << iter << "\n";
    QueryPerformanceCounter((LARGE_INTEGER *)&endTick); // Get end tick
    totalTicks += endTick - startTick; // accumulate time taken
}

// convert to elapsed microseconds
__int64 totalMicroSeconds =  (totalTicks * 1000000ull)/ ticksPerSecond;

__int64 hours = totalMicroSeconds / us_per_hour;
totalMicroSeconds %= us_per_hour;
__int64 minutes = totalMicroSeconds / us_per_min;
totalMicroSeconds %= us_per_min;
__int64 seconds = totalMicroSeconds / us_per_sec;
totalMicroSeconds %= us_per_sec;
__int64 milliseconds = totalMicroSeconds / us_per_ms;
totalMicroSeconds %= us_per_ms;


std::cout << "Total time: " << hours << "h ";
std::cout << minutes << "m " << seconds << "s " << milliseconds << "ms ";
std::cout << totalMicroSeconds << "us\n";

return 0;
}

2

Kompletnym niezawodnym rozwiązaniem planowania wątków, które powinno dawać dokładnie takie same czasy w każdym teście, jest skompilowanie programu, aby był niezależny od systemu operacyjnego i uruchomienie komputera w celu uruchomienia programu w środowisku wolnym od systemu operacyjnego. Jest to jednak w dużej mierze niepraktyczne i co najwyżej trudne.

Dobrym substytutem przejścia bez systemu operacyjnego jest po prostu ustawienie koligacji bieżącego wątku na 1 rdzeń i priorytet na najwyższy. Ta alternatywa powinna zapewnić wystarczająco spójne wyniki.

Powinieneś także wyłączyć optymalizacje, które kolidowałyby z debugowaniem, co w przypadku g ++ lub gcc oznacza dodanie -Ogdo wiersza poleceń , aby zapobiec optymalizacji testowanego kodu. -O0Flaga nie powinna być stosowana, ponieważ wprowadza dodatkowe niepotrzebne obciążenie, które zostaną uwzględnione w wynikach pomiaru czasu, a tym samym pochylanie się czasowe szybkości kodu.

Wręcz przeciwnie, zarówno zakładając, że używasz -Ofast(lub przynajmniej -O3) w końcowej kompilacji produkcyjnej, jak i ignorując kwestię eliminacji „martwego” kodu, -Ogprzeprowadza bardzo niewiele optymalizacji w porównaniu z -Ofast; w ten sposób -Ogmoże fałszywie przedstawić rzeczywistą prędkość kodu w produkcie końcowym.

Co więcej, wszystkie testy szybkości (do pewnego stopnia) krzywoprzysięstwa: w skompilowanym produkcie końcowym -Ofastkażdy fragment / sekcja / funkcja kodu nie jest izolowana; raczej każdy fragment kodu nieustannie przepływa do następnego, umożliwiając w ten sposób kompilatorowi potencjalne łączenie, scalanie i optymalizowanie razem fragmentów kodu z każdego miejsca.

W tym samym czasie, jeśli porównujesz fragment kodu, który jest intensywnie używany realloc(), fragment kodu może działać wolniej w produkcie produkcyjnym z wystarczająco dużą fragmentacją pamięci. Stąd wyrażenie „całość to więcej niż suma jej części” odnosi się do tej sytuacji, ponieważ kod w końcowej wersji produkcyjnej może działać zauważalnie szybciej lub wolniej niż pojedynczy fragment kodu, który szybko testujesz.

Częściowym rozwiązaniem, które może zmniejszyć niezgodność, jest użycie -Ofastdo testowania szybkości z dodatkiem asm volatile("" :: "r"(var))do zmiennych biorących udział w teście w celu zapobieżenia eliminacji martwego kodu / pętli.

Oto przykład testu porównawczego funkcji pierwiastka kwadratowego na komputerze z systemem Windows.

// set USE_ASM_TO_PREVENT_ELIMINATION  to 0 to prevent `asm volatile("" :: "r"(var))`
// set USE_ASM_TO_PREVENT_ELIMINATION  to 1 to enforce `asm volatile("" :: "r"(var))`
#define USE_ASM_TO_PREVENT_ELIMINATION 1

#include <iostream>
#include <iomanip>
#include <cstdio>
#include <chrono>
#include <cmath>
#include <windows.h>
#include <intrin.h>
#pragma intrinsic(__rdtsc)
#include <cstdint>

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

unsigned int guess_sqrt32(register unsigned int n) {
    register unsigned int g = 0x8000;
    if(g*g > n) {
        g ^= 0x8000;
    }
    g |= 0x4000;
    if(g*g > n) {
        g ^= 0x4000;
    }
    g |= 0x2000;
    if(g*g > n) {
        g ^= 0x2000;
    }
    g |= 0x1000;
    if(g*g > n) {
        g ^= 0x1000;
    }
    g |= 0x0800;
    if(g*g > n) {
        g ^= 0x0800;
    }
    g |= 0x0400;
    if(g*g > n) {
        g ^= 0x0400;
    }
    g |= 0x0200;
    if(g*g > n) {
        g ^= 0x0200;
    }
    g |= 0x0100;
    if(g*g > n) {
        g ^= 0x0100;
    }
    g |= 0x0080;
    if(g*g > n) {
        g ^= 0x0080;
    }
    g |= 0x0040;
    if(g*g > n) {
        g ^= 0x0040;
    }
    g |= 0x0020;
    if(g*g > n) {
        g ^= 0x0020;
    }
    g |= 0x0010;
    if(g*g > n) {
        g ^= 0x0010;
    }
    g |= 0x0008;
    if(g*g > n) {
        g ^= 0x0008;
    }
    g |= 0x0004;
    if(g*g > n) {
        g ^= 0x0004;
    }
    g |= 0x0002;
    if(g*g > n) {
        g ^= 0x0002;
    }
    g |= 0x0001;
    if(g*g > n) {
        g ^= 0x0001;
    }
    return g;
}

unsigned int empty_function( unsigned int _input ) {
    return _input;
}

unsigned long long empty_ticks=0;
double empty_seconds=0;
Timer my_time;

template<unsigned int benchmark_repetitions>
void benchmark( char* function_name, auto (*function_to_do)( auto ) ) {
    register unsigned int i=benchmark_repetitions;
    register unsigned long long start=0;
    my_time.reset();
    start=__rdtsc();
    while ( i-- ) {
        auto result = (*function_to_do)( i << 7 );
        #if USE_ASM_TO_PREVENT_ELIMINATION == 1
            asm volatile("" :: "r"(
                // There is no data type in C++ that is smaller than a char, so it will
                //  not throw a segmentation fault error to reinterpret any arbitrary
                //  data type as a char. Although, the compiler might not like it.
                result
            ));
        #endif
    }
    if ( function_name == nullptr ) {
        empty_ticks = (__rdtsc()-start);
        empty_seconds = my_time.elapsed();
        std::cout<< "Empty:\n" << empty_ticks
              << " ticks\n" << benchmark_repetitions << " repetitions\n"
               << std::setprecision(15) << empty_seconds
                << " seconds\n\n";
    } else {
        std::cout<< function_name<<":\n" << (__rdtsc()-start-empty_ticks)
              << " ticks\n" << benchmark_repetitions << " repetitions\n"
               << std::setprecision(15) << (my_time.elapsed()-empty_seconds)
                << " seconds\n\n";
    }
}


int main( void ) {
    void* Cur_Thread=   GetCurrentThread();
    void* Cur_Process=  GetCurrentProcess();
    unsigned long long  Current_Affinity;
    unsigned long long  System_Affinity;
    unsigned long long furthest_affinity;
    unsigned long long nearest_affinity;

    if( ! SetThreadPriority(Cur_Thread,THREAD_PRIORITY_TIME_CRITICAL) ) {
        SetThreadPriority( Cur_Thread, THREAD_PRIORITY_HIGHEST );
    }
    if( ! SetPriorityClass(Cur_Process,REALTIME_PRIORITY_CLASS) ) {
        SetPriorityClass( Cur_Process, HIGH_PRIORITY_CLASS );
    }
    GetProcessAffinityMask( Cur_Process, &Current_Affinity, &System_Affinity );
    furthest_affinity = 0x8000000000000000ULL>>__builtin_clzll(Current_Affinity);
    nearest_affinity  = 0x0000000000000001ULL<<__builtin_ctzll(Current_Affinity);
    SetProcessAffinityMask( Cur_Process, furthest_affinity );
    SetThreadAffinityMask( Cur_Thread, furthest_affinity );

    const int repetitions=524288;

    benchmark<repetitions>( nullptr, empty_function );
    benchmark<repetitions>( "Standard Square Root", standard_sqrt );
    benchmark<repetitions>( "Original Guess Square Root", original_guess_sqrt32 );
    benchmark<repetitions>( "New Guess Square Root", new_guess_sqrt32 );


    SetThreadPriority( Cur_Thread, THREAD_PRIORITY_IDLE );
    SetPriorityClass( Cur_Process, IDLE_PRIORITY_CLASS );
    SetProcessAffinityMask( Cur_Process, nearest_affinity );
    SetThreadAffinityMask( Cur_Thread, nearest_affinity );
    for (;;) { getchar(); }

    return 0;
}

Również uznanie dla Mike'a Jarvisa za jego Timer.

Pamiętaj (jest to bardzo ważne), że jeśli zamierzasz uruchamiać większe fragmenty kodu, to naprawdę musisz zmniejszyć liczbę iteracji, aby zapobiec zawieszaniu się komputera.


2
Dobra odpowiedź, z wyjątkiem wyłączenia optymalizacji. Benchmarking -O0kod jest duża strata czasu, ponieważ narzut -O0 zamiast normalnego -O2lub -O3 -march=nativezmienia się gwałtownie w zależności od kodu i nakładu pracy. np. dodatkowe nazwane zmienne tmp kosztują czas w -O0. Istnieją inne sposoby uniknięcia optymalizacji elementów, takie jak ukrywanie rzeczy przed optymalizatorem za pomocą volatilefunkcji nieliniowych lub pustych instrukcji asm wbudowanych. -O0nie jest nawet blisko użyteczna, ponieważ kod ma różne wąskie gardła -O0, nie takie same, ale gorsze.
Peter Cordes

1
Ugh, -Ognadal nie jest zbyt realistyczne, w zależności od kodu. Przynajmniej -O2najlepiej -O3jest bardziej realistyczne. Użyj asm volatile("" ::: "+r"(var))lub czegoś, aby kompilator zmaterializował wartość w rejestrze i pokonał ciągłą propagację przez nią.
Peter Cordes

@PeterCordes Jeszcze raz dziękuję za spostrzeżenia. Zaktualizowałem zawartość za pomocą, -O3a fragment kodu za pomocą asm volatile("" ::: "+r"(var)).
Jack Giffin

1
asm volatile("" ::: "+r"( i ));wydaje się niepotrzebne. W zoptymalizowanym kodzie nie ma powodu, aby zmuszać kompilator do materializacji, itak samo jak i<<7wewnątrz pętli. Zatrzymujesz go od optymalizacji na tmp -= 128zamiast przesuwania za każdym razem. Użycie wyniku wywołania funkcji jest jednak dobre, jeśli nie jest void. Lubię int result = (*function_to_do)( i << 7 );. Możesz użyć asmoświadczenia na temat tego wyniku.
Peter Cordes

@PeterCordes Jeszcze raz bardzo dziękuję za twoje spostrzeżenia. Mój post zawiera teraz poprawki dla wartości zwracanej z, function_to_dodzięki czemu function_to_domożna je wstawiać bez eliminacji. Daj mi znać, jeśli masz dalsze sugestie.
Jack Giffin

1

W przypadkach, w których chcesz mierzyć ten sam odcinek kodu za każdym razem, gdy jest on wykonywany (np. W celu profilowania kodu, który Twoim zdaniem może stanowić wąskie gardło), oto wrapper (niewielka modyfikacja) funkcji Andreasa Boniniego, która jest przydatna:

#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/time.h>
#endif

/*
 *  A simple timer class to see how long a piece of code takes. 
 *  Usage:
 *
 *  {
 *      static Timer timer("name");
 *
 *      ...
 *
 *      timer.start()
 *      [ The code you want timed ]
 *      timer.stop()
 *
 *      ...
 *  }
 *
 *  At the end of execution, you will get output:
 *
 *  Time for name: XXX seconds
 */
class Timer
{
public:
    Timer(std::string name, bool start_running=false) : 
        _name(name), _accum(0), _running(false)
    {
        if (start_running) start();
    }

    ~Timer() { stop(); report(); }

    void start() {
        if (!_running) {
            _start_time = GetTimeMicroseconds();
            _running = true;
        }
    }
    void stop() {
        if (_running) {
            unsigned long long stop_time = GetTimeMicroseconds();
            _accum += stop_time - _start_time;
            _running = false;
        }
    }
    void report() { 
        std::cout<<"Time for "<<_name<<": " << _accum / 1.e6 << " seconds\n"; 
    }
private:
    // cf. http://stackoverflow.com/questions/1861294/how-to-calculate-execution-time-of-a-code-snippet-in-c
    unsigned long long GetTimeMicroseconds()
    {
#ifdef _WIN32
        /* Windows */
        FILETIME ft;
        LARGE_INTEGER li;

        /* Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC) and copy it
         *   * to a LARGE_INTEGER structure. */
        GetSystemTimeAsFileTime(&ft);
        li.LowPart = ft.dwLowDateTime;
        li.HighPart = ft.dwHighDateTime;

        unsigned long long ret = li.QuadPart;
        ret -= 116444736000000000LL; /* Convert from file time to UNIX epoch time. */
        ret /= 10; /* From 100 nano seconds (10^-7) to 1 microsecond (10^-6) intervals */
#else
        /* Linux */
        struct timeval tv;

        gettimeofday(&tv, NULL);

        unsigned long long ret = tv.tv_usec;
        /* Adds the seconds (10^0) after converting them to microseconds (10^-6) */
        ret += (tv.tv_sec * 1000000);
#endif
        return ret;
    }
    std::string _name;
    long long _accum;
    unsigned long long _start_time;
    bool _running;
};

1

po prostu prosta klasa, która porównuje blok kodu:

using namespace std::chrono;

class benchmark {
  public:
  time_point<high_resolution_clock>  t0, t1;
  unsigned int *d;
  benchmark(unsigned int *res) : d(res) { 
                 t0 = high_resolution_clock::now();
  }
  ~benchmark() { t1 = high_resolution_clock::now();
                  milliseconds dur = duration_cast<milliseconds>(t1 - t0);
                  *d = dur.count();
  }
};
// simple usage 
// unsigned int t;
// { // put the code in a block
//  benchmark bench(&t);
//  // ...
//  // code to benchmark
// }
// HERE the t contains time in milliseconds

// one way to use it can be :
#define BENCH(TITLE,CODEBLOCK) \
  unsigned int __time__##__LINE__ = 0;  \
  { benchmark bench(&__time__##__LINE__); \
      CODEBLOCK \
  } \
  printf("%s took %d ms\n",(TITLE),__time__##__LINE__);


int main(void) {
  BENCH("TITLE",{
    for(int n = 0; n < testcount; n++ )
      int a = n % 3;
  });
  return 0;
}

0

boost :: timer prawdopodobnie zapewni Ci taką dokładność, jakiej potrzebujesz. Nie jest wystarczająco dokładne, aby powiedzieć, ile czasu a = a+1;to zajmie, ale z jakiego powodu miałbyś mieć czas na coś, co zajmuje kilka nanosekund?


Opiera się na clock()funkcji ze standardowego nagłówka C ++.
Petter

0

Utworzyłem lambdę, która wywołuje wywołanie funkcji N razy i zwraca średnią.

double c = BENCHMARK_CNT(25, fillVectorDeque(variable));

Możesz znaleźć nagłówek c ++ 11 tutaj .


0

Stworzyłem proste narzędzie do pomiaru wydajności bloków kodu, korzystając z funkcji high_resolution_clock biblioteki chrono: https://github.com/nfergu/codetimer .

Czasy można rejestrować dla różnych kluczy i można wyświetlić zbiorczy widok czasów dla każdego klawisza.

Sposób użycia jest następujący:

#include <chrono>
#include <iostream>
#include "codetimer.h"

int main () {
    auto start = std::chrono::high_resolution_clock::now();
    // some code here
    CodeTimer::record("mykey", start);
    CodeTimer::printStats();
    return 0;
}

0

Możesz również przyjrzeć się [cxx-rtimers][1]witrynie GitHub, która udostępnia pewne procedury zawierające tylko nagłówki do zbierania statystyk dotyczących czasu wykonywania dowolnego bloku kodu, w którym można utworzyć zmienną lokalną. Te timery mają wersje, które używają std :: chrono w C ++ 11, timerów z biblioteki Boost lub standardowych funkcji timera POSIX. Te liczniki czasu będą zgłaszać średni, maksymalny i minimalny czas spędzony w funkcji, a także liczbę wywołań. Można ich używać w następujący sposób:

#include <rtimers/cxx11.hpp>

void expensiveFunction() {
    static rtimers::cxx11::DefaultTimer timer("expensive");
    auto scopedStartStop = timer.scopedStart();
    // Do something costly...
}

0

Tak to robię, mało kodu, łatwy do zrozumienia, pasuje do moich potrzeb:

void bench(std::function<void()> fnBench, std::string name, size_t iterations)
{
    if (iterations == 0)
        return;
    if (fnBench == nullptr)
        return;
    std::chrono::high_resolution_clock::time_point start, end;
    if (iterations == 1)
    {
        start = std::chrono::high_resolution_clock::now();
        fnBench();
        end = std::chrono::high_resolution_clock::now();
    }
    else
    {
        start = std::chrono::high_resolution_clock::now();
        for (size_t i = 0; i < iterations; ++i)
            fnBench();
        end = std::chrono::high_resolution_clock::now();
    }
    printf
    (
        "bench(*, \"%s\", %u) = %4.6lfs\r\n",
        name.c_str(),
        iterations,
        std::chrono::duration_cast<std::chrono::duration<double>>(end - start).count()
    );
}

Stosowanie:

bench
(
    []() -> void // function
    {
        // Put your code here
    },
    "the name of this", // name
    1000000 // iterations
);

0
#include <omp.h>

double start = omp_get_wtime();

// code 

double finish = omp_get_wtime();

double total_time = finish - start;

2
Chociaż ten kod może rozwiązać problem, w tym wyjaśnienie, jak i dlaczego to rozwiązuje problem, naprawdę pomogłoby poprawić jakość twojego posta i prawdopodobnie zaowocowałoby większą liczbą pozytywnych głosów. Pamiętaj, że odpowiadasz na pytanie do czytelników w przyszłości, a nie tylko osoba, która zapyta teraz. Proszę edytować swoje odpowiedzi, aby dodać wyjaśnień i dać wskazówkę co zastosować ograniczenia i założenia.
Dharman
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.