Измерьте время выполнения произвольных функций с помощью C ++ 14 лямбда - PullRequest
0 голосов
/ 12 декабря 2018

Я был взволнован пунктом 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 ]

0 голосов
/ 12 декабря 2018

Существует два способа решения этой задачи.

  1. Принять функцию (или объект функции), вернуть модифицированную функцию, которая выполняет ту же функцию, что и оригинальная функция, плюс мерывремя.Возвращаемый тип объекта не может совпадать с принятым типом параметра.Это должна быть лямбда (или пользовательский тип класса, но лямбда проще).Фактическое измерение выполняется, когда вызывается возвращенный объект.Пример использования синтаксиса:

    result = measure(foo)(param1, param2);  // variant 1
    auto measured_foo = measure(foo);
    result = measured_foo(param1, param2);  // variant 2
    
  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 для создания связанной функции-члена.Существует около миллиона вопросов (и отличных ответов) о правильном способе сделать это в stackoverflow.

Live демо (с возвратом по ссылке работает).

Если выЧтобы получить первый способ создания измеряемой функции, выполните следующие действия:

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 во второй версии.

0 голосов
/ 12 декабря 2018

Понятия не имею, что вы пытаетесь сделать там, но я полагаю, что здесь я сделал то, что сделал, если это то, что вы пытаетесь сделать:

#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;
}
...