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ą?
clock()
nie jest tak szybko, jak myślałem.Odpowiedzi:
To, co inni opublikowali na temat wielokrotnego uruchamiania funkcji w pętli, jest poprawne.
W przypadku Linuksa (i BSD) chcesz użyć clock_gettime () .
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:
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
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.
źródło
CLOCK_MONOTONIC_RAW
, jeśli jest dostępny, aby uzyskać czas sprzętowy nieskorygowany przez NTP.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 funkcjiRDTSC
wspomnianej w kilku innych odpowiedziach tutaj. Więc pomyślałem, że pokażę jak korzystaćRDTSC
z<chrono>
. Dodatkowo będę pokazują, jak można templatize kod testowy na zegarze, dzięki czemu można szybko przełączać się międzyRDTSC
a system wbudowany w obiektach zegar (który prawdopodobnie zostanie na podstawieclock()
,clock_gettime()
i / lubQueryPerformanceCounter
.Zwróć uwagę, że
RDTSC
instrukcja jest specyficzna dla x86.QueryPerformanceCounter
dotyczy tylko systemu Windows. Iclock_gettime()
jest tylko POSIX. Poniżej przedstawiam dwa nowe zegary:std::chrono::high_resolution_clock
istd::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
rdtsc
instrukcji montażu Intela . Nazwę tox::clock
: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: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::nanoseconds
mogę 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
Cycle
w kodzie).Pętla jest synchronizowana z wywołaniami
clock::now()
po obu stronach. Jeśli chcesz nazwać typ zwracany przez tę funkcję, jest to:(jak wyraźnie widać w
x::clock
przykł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_cast
aby przekonwertować czas trwaniaCycle
na czas trwaniapicoseconds
i wydrukować to.Korzystanie z tego kodu jest proste:
Powyżej wykonuję test na naszej domowej roboty
x::clock
i porównuję te wyniki z dwoma zegarami dostarczonymi przez system:std::chrono::high_resolution_clock
istd::chrono::system_clock
. Dla mnie to drukuje: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:
x::clock
.źródło
rdtsc
Zegar 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).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ą.
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 ().
źródło
Aby zrobić to poprawnie, możesz użyć jednego z dwóch sposobów, albo z,
RDTSC
albo zclock_gettime()
. Drugi jest około 2 razy szybszy i ma tę zaletę, że podaje właściwy czas absolutny. Zauważ, żeRDTSC
aby 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)i dla clock_gettime: (arbitralnie wybrałem rozdzielczość mikrosekund)
czas i uzyskane wartości:
źródło
Aby uzyskać pożądane rezultaty, używam:
źródło
Dla C ++ 11 , oto prosta otoka:
Lub dla C ++ 03 na * nix,
Przykład użycia:
Z https://gist.github.com/gongzhitaao/7062087
źródło
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.
źródło
Możesz użyć następującej funkcji z gcc działającym na procesorach x86:
z Digital Mars C ++:
który odczytuje licznik czasu o wysokiej wydajności w chipie. Używam tego podczas profilowania.
źródło
unsigned int
jako typu wewnętrznego.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 .
źródło
Używam kodu Borlanda, tutaj kod ti_hund podaje mi czasami liczbę ujemną, ale czas jest dość dobry.
źródło
Korzystanie z metody Brocka Adamsa z prostą klasą:
Przykład użycia:
Wynik:
test trwał: 0,0002 ms
Ma narzut na wywołanie funkcji, ale powinien być wystarczająco szybki :)
źródło
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ę:
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.
źródło
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.
źródło
Co myślicie o tym:
źródło
Oto ładny timer doładowania, który działa dobrze:
źródło
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
, lubhours
.Stosowanie
Standardowy wynik wyjściowy
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 ()):
Możesz więc po prostu użyć:
Który wyświetli wszystkie znaki, tak jak poprzednio, ale po wykonaniu drugiego kodu. Pamiętaj, że nie powinieneś używać obu
m.s()
im.t()
.źródło