W książce Scotta Meyersa znalazłem przykład uniwersalnego generycznego wyrażenia lambda, którego można użyć do pomiaru czasu wykonywania funkcji. (C ++ 14)
auto timeFuncInvocation =
[](auto&& func, auto&&... params) {
const auto& start = std::chrono::high_resolution_clock::now();
std::forward<decltype(func)>(func)(std::forward<decltype(params)>(params)...);
const auto& stop = std::chrono::high_resolution_clock::now();
return stop - start;
};
Problem polega na tym, że mierzysz tylko jedno wykonanie, więc wyniki mogą być bardzo różne. Aby uzyskać wiarygodny wynik, należy zmierzyć dużą liczbę wykonań. Według wykładu Andrei Alexandrescu na konferencji code :: dive 2015 - Writing Fast Code I:
Zmierzony czas: tm = t + tq + tn + to
gdzie:
tm - zmierzony (obserwowany) czas
t - rzeczywisty czas zainteresowania
tq - czas dodany przez szum kwantyzacji
tn - czas dodawany przez różne źródła hałasu
to - czas narzutu (pomiar, pętla, wywołanie funkcji)
Zgodnie z tym, co powiedział w dalszej części wykładu, jako wynik należy przyjąć minimum tej dużej liczby egzekucji. Zachęcam do obejrzenia wykładu, w którym wyjaśnia dlaczego.
Jest też bardzo dobra biblioteka Google - https://github.com/google/benchmark . Ta biblioteka jest bardzo prosta w użyciu i potężna. Możesz sprawdzić niektóre wykłady Chandlera Carrutha na youtube, gdzie korzysta z tej biblioteki w praktyce. Na przykład CppCon 2017: Chandler Carruth „Going Nowhere Faster”;
Przykładowe użycie:
#include <iostream>
#include <chrono>
#include <vector>
auto timeFuncInvocation =
[](auto&& func, auto&&... params) {
const auto& start = high_resolution_clock::now();
for(auto i = 0; i < 100000; ++i) {
std::forward<decltype(func)>(func)(std::forward<decltype(params)>(params)...);
}
const auto& stop = high_resolution_clock::now();
return (stop - start)/100000;
};
void f(std::vector<int>& vec) {
vec.push_back(1);
}
void f2(std::vector<int>& vec) {
vec.emplace_back(1);
}
int main()
{
std::vector<int> vec;
std::vector<int> vec2;
std::cout << timeFuncInvocation(f, vec).count() << std::endl;
std::cout << timeFuncInvocation(f2, vec2).count() << std::endl;
std::vector<int> vec3;
vec3.reserve(100000);
std::vector<int> vec4;
vec4.reserve(100000);
std::cout << timeFuncInvocation(f, vec3).count() << std::endl;
std::cout << timeFuncInvocation(f2, vec4).count() << std::endl;
return 0;
}
EDYCJA: Oczywiście zawsze musisz pamiętać, że Twój kompilator może coś zoptymalizować lub nie. W takich przypadkach przydatne mogą być narzędzia takie jak perf.
clock_gettime
.. gcc definiuje inne zegary jako:typedef system_clock steady_clock; typedef system_clock high_resolution_clock;
w Windows użyjQueryPerformanceCounter
.