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?
c++
benchmarking
AhmetB - Google
źródło
źródło
Odpowiedzi:
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 - podobnietime(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.
źródło
gettimeofday
może dać niezamierzony wynik, jeśli zegar systemowy zostanie zmieniony. Jeśli byłby to dla Ciebie problem, możeszclock_gettime
zamiast tego przyjrzeć się .GetTickCount
?gcc -std=c99
GetTickCount
to 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.Mam inny działający przykład, który używa mikrosekund (UNIX, POSIX itp.).
Oto plik, w którym to zakodowaliśmy:
https://github.com/arhuaco/junkcode/blob/master/emqbit-bench/bench.c
źródło
#include <sys/time.h>
na początku swojego przykładu.Oto proste rozwiązanie w C ++ 11, które daje satysfakcjonującą rozdzielczość.
Lub na * nix, dla c ++ 03
Oto przykład użycia:
Z https://gist.github.com/gongzhitaao/7062087
źródło
/usr/lib/x86_64-linux-gnu/libstdc++.so.6: version GLIBCXX_3.4.19 not found (required by ../cpu_2d/g500)
Gdy
progress_timer
wyjdzie 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):
źródło
Windows udostępnia funkcję QueryPerformanceCounter (), a Unix - gettimeofday () Obie funkcje mogą mierzyć różnicę co najmniej 1 mikro-sekundy.
źródło
#ifdef
musi 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
.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 )
źródło
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.
źródło
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.
źródło
(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
źródło
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
-Og
do wiersza poleceń , aby zapobiec optymalizacji testowanego kodu.-O0
Flaga 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,-Og
przeprowadza bardzo niewiele optymalizacji w porównaniu z-Ofast
; w ten sposób-Og
moż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
-Ofast
każ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
-Ofast
do testowania szybkości z dodatkiemasm 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.
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.
źródło
-O0
kod jest duża strata czasu, ponieważ narzut-O0
zamiast normalnego-O2
lub-O3 -march=native
zmienia 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ąvolatile
funkcji nieliniowych lub pustych instrukcji asm wbudowanych.-O0
nie jest nawet blisko użyteczna, ponieważ kod ma różne wąskie gardła-O0
, nie takie same, ale gorsze.-Og
nadal nie jest zbyt realistyczne, w zależności od kodu. Przynajmniej-O2
najlepiej-O3
jest bardziej realistyczne. Użyjasm volatile("" ::: "+r"(var))
lub czegoś, aby kompilator zmaterializował wartość w rejestrze i pokonał ciągłą propagację przez nią.-O3
a fragment kodu za pomocąasm volatile("" ::: "+r"(var))
.asm volatile("" ::: "+r"( i ));
wydaje się niepotrzebne. W zoptymalizowanym kodzie nie ma powodu, aby zmuszać kompilator do materializacji,i
tak samo jaki<<7
wewnątrz pętli. Zatrzymujesz go od optymalizacji natmp -= 128
zamiast przesuwania za każdym razem. Użycie wyniku wywołania funkcji jest jednak dobre, jeśli nie jestvoid
. Lubięint result = (*function_to_do)( i << 7 );
. Możesz użyćasm
oświadczenia na temat tego wyniku.function_to_do
dzięki czemufunction_to_do
można je wstawiać bez eliminacji. Daj mi znać, jeśli masz dalsze sugestie.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:
źródło
po prostu prosta klasa, która porównuje blok kodu:
źródło
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?źródło
clock()
funkcji ze standardowego nagłówka C ++.Utworzyłem lambdę, która wywołuje wywołanie funkcji N razy i zwraca średnią.
Możesz znaleźć nagłówek c ++ 11 tutaj .
źródło
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:
źródło
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:źródło
Tak to robię, mało kodu, łatwy do zrozumienia, pasuje do moich potrzeb:
Stosowanie:
źródło
źródło