Измерьте время выполнения произвольных функций с помощью C++14 лямбда
Я был взволнован пунктом 24 книги Скотта Мейера "Эффективный современный C++". Он упоминает о возможности написания лямбды C++14 для записи времени, затраченного на произвольный вызов функции.
Я все еще изучаю особенности C++14. Моя попытка (Main.cpp) выглядит следующим образом для измерения времени вызова функции-члена:
#include <chrono>
#include <iostream>
auto measure = [](auto&& function, auto&&... parameters) -> decltype(function)
{
const std::chrono::steady_clock::time_point startTimePoint =
std::chrono::steady_clock::now();
const auto returnValue = std::forward<decltype(function)>(function)(
std::forward<decltype(parameters)>(parameters)...);
const std::chrono::steady_clock::time_point stopTimePoint =
std::chrono::steady_clock::now();
const std::chrono::duration<double> timeSpan = std::chrono::duration_cast<
std::chrono::duration<double>>(stopTimePoint - startTimePoint);
std::cout << "Computation took " << timeSpan.count()
<< " seconds." << std::endl;
return returnValue;
};
class Test
{
public:
int computation(double dummy)
{
std::cout << "Received " << dummy << ". Computing..." << std::endl;
return 123;
}
};
int main(int, char**)
{
Test instance;
using Function = int (Test::*)(double);
Function function = instance.computation;
int result = measure(function, 1.0);
std::cout << "Result: " << result << std::endl;
return 0;
}
Я получаю следующие ошибки компиляции:
..\src\Main.cpp: In function 'int main(int, char**)':
..\src\Main.cpp:43:36: error: cannot convert 'int (Test::*)(double)' to 'int' in initialization
int result = measure(function, 1.0);
^
..\src\Main.cpp: In instantiation of '<lambda(auto:1&&, auto:2&& ...)> [with auto:1 = int (Test::*&)(double); auto:2 = {double}; decltype (function) = int (Test::*&)(double)]':
..\src\Main.cpp:43:36: required from here
..\src\Main.cpp:9:69: error: must use '.*' or '->*' to call pointer-to-member function in 'std::forward<int (Test::*&)(double)>((* & function)) (...)', e.g. '(... ->* std::forward<int (Test::*&)(double)>((* & function))) (...)'
const auto returnValue = std::forward<decltype(function)>(function)(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
std::forward<decltype(parameters)>(parameters)...);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Очевидно, я делаю это неправильно, но я не мог понять, как сделать это правильно. Кто-нибудь может мне помочь? Большое спасибо!
2 ответа
Есть два способа решения этой задачи.
Примите функцию (или функциональный объект), верните модифицированную функцию, которая делает то же самое, что и оригинальная функция, плюс измеряет время. Возвращаемый тип объекта не может совпадать с принятым типом параметра. Это должна быть лямбда (или пользовательский тип класса, но лямбда проще). Фактическое измерение выполняется при вызове возвращаемого объекта. Пример использования синтаксиса:
result = measure(foo)(param1, param2); // variant 1 auto measured_foo = measure(foo); result = measured_foo(param1, param2); // variant 2
Примите функцию (или функциональный объект) и ее параметры, вызовите ее и выполните измерение. Тип возвращаемого значения - это тип исходной функции. Пример использования синтаксиса:
result = measure(foo, param1, param2);
Ваш measure
ближе всего ко второму варианту, единственное, что не так с ним - это объявление. Это правильный:
auto measure = [](auto&& function, auto&&... parameters) -> decltype(auto)
Чтобы быть точным, это не единственная неправильная вещь. Если измеренная функция возвращает ссылку, тип возврата будет неправильным. Чтобы исправить это, замените
const auto returnValue = ...
с
decltype(auto) returnValue = ...
в теле лямбды
Другая вещь, которая не так с вашей программой (но не measure
само по себе) - это способ, которым вы пытаетесь использовать функцию-член.
Function function = instance.computation;
Это просто не работает. Используйте лямбду или std::bind
создать связанную функцию-член. Существует около миллиона вопросов (и отличных ответов) о правильном способе сделать это в стеке потока.
Живая демоверсия (с возвратом по ссылке рабочая).
Если вы хотите первый способ создания измеряемой функции, вот как:
auto measure = [](auto&& function) -> decltype(auto)
{
return [=](auto&&... parameters) mutable -> decltype(auto) {
const std::chrono::steady_clock::time_point startTimePoint =
std::chrono::steady_clock::now();
decltype(auto) result = function(std::forward<decltype(parameters)>(parameters)...);
const std::chrono::steady_clock::time_point stopTimePoint =
std::chrono::steady_clock::now();
const std::chrono::duration<double> timeSpan = std::chrono::duration_cast<
std::chrono::duration<double>>(stopTimePoint - startTimePoint);
std::cout << "Computation took " << timeSpan.count()
<< " seconds." << std::endl;
return result;
};
};
Живая демоверсия (с возвратом по ссылке).
Обратите особое внимание на религиозное использование decltype(auto)
, Также mutable
во второй версии.
Понятия не имею, что вы пытаетесь делать там, но я полагаю, что я сделал здесь, если это то, что вы пытаетесь сделать:
#include <chrono>
#include <iostream>
#include <functional>
auto measure = [](auto function, auto&&... parameters) -> decltype(function(parameters...))
{
const std::chrono::steady_clock::time_point startTimePoint =
std::chrono::steady_clock::now();
auto returnValue = function(parameters...);
const std::chrono::steady_clock::time_point stopTimePoint =
std::chrono::steady_clock::now();
const std::chrono::duration<double> timeSpan = std::chrono::duration_cast<
std::chrono::duration<double>>(stopTimePoint - startTimePoint);
std::cout << "Computation took " << timeSpan.count()
<< " seconds." << std::endl;
return returnValue;
};
class Test
{
public:
int computation(double dummy)
{
std::cout << "Received " << dummy << ". Computing..." << std::endl;
return 123;
}
};
int main(int, char**)
{
Test instance;
auto func = std::bind(&Test::computation, &instance, std::placeholders::_1);
int result = measure(func, 1.0);
std::cout << "Result: " << result << std::endl;
return 0;
}
Если вам не нравится использовать указатель функции функции-члена (см. Здесь) по какой-либо причине, старые добрые макросы могут прийти вам на помощь. Некоторые люди предлагают свести к минимуму использование макросов, но в этом случае я обнаружил, что это более интуитивно понятно, читается и легко. Любая произвольная функция (включая вызов общедоступной функции-члена класса, возвращающего тип) может быть синхронизирована с помощью следующего макроса.
#define timefnrt(W, X, Z){\
time_t V = time(NULL);\
W = X;\
time_t Y = time(NULL);\
Z = difftime(Y, V);\
};
В случае, если функция возвращает void
, такие функции могут быть синхронизированы таким образом:
#define timefnvoid(X, Z){\
time_t V = time(NULL);\
X;\
time_t Y = time(NULL);\
Z = difftime(Y, V);\
};