简体   繁体   中英

Measure execution time of arbitrary functions with C++14 lambda

I have been excited by item 24 of Scott Meyer's book "Effective Modern C++". He mentions the possibility to write a C++14 lambda to record the time taken in an arbitrary function invocation.

I am still in an early of learning C++14 features. My attempt (Main.cpp) looks like this for measuring the time of a member function call:

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

I get the following compilation errors:

..\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)...);
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                

Obviously I am doing it wrong, but I could not figure out how to do it right. Can anybody help me? Thank you very much!

There are two ways to approach this task.

  1. Accept a function (or a function object), return a modified function that does the same thing the original function does, plus measures the time. The returned object type cannot be the same as the accepted parameter type. It must be a lambda (or a custom class type, but the lambda is simpler). The actual measurement is performed when the returned object is invoked. Example usage syntax:

     result = measure(foo)(param1, param2); // variant 1 auto measured_foo = measure(foo); result = measured_foo(param1, param2); // variant 2
  2. Accept a function (or a function object) and its parameters, call it and perform the measurement. The return type is that of the original function. Example usage syntax:

     result = measure(foo, param1, param2);

Your measure is closest to the second variant, the only thing that is wrong with it is the declaration. This is the correct one:

auto measure = [](auto&& function, auto&&... parameters) -> decltype(auto)

To be precise, this is not the only wrong thing. If the measured function returns a reference, the return type will be wrong. To fix this, replace

const auto returnValue = ...

with

decltype(auto) returnValue = ...

in the body of the lambda

The other thing that is wrong with your program (but not the measure itself) is the way you are trying to use a member function.

Function function = instance.computation;

This just doesn't work. Use a lambda or std::bind to create a bound member function. There's about a zillion questions (and great answers) about the correct way to do it on stackoverflow.

Live demo (with return by reference working).

If you want the first way of creating a measured function, here's how:

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

Live demo (with return by reference).

Pay special attention to religious use of decltype(auto) . Also mutable in the second version.

Have no clue what you are trying to do there but I am taking my guess here what I have done if this is what you are trying to do:

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

If you dislike taking a function pointer of a member function (see here ) for any reason, good old macros can come to your rescue. Some folks suggest to minimize macro usage, but in this case, I have found it to be more intuitive and readable and easy. Any arbitrary function (including call to a public member function of a class that returns a type) can be timed using the following macro.

#define timefnrt(W, X, Z){\
    time_t V = time(NULL);\
    W = X;\
    time_t Y = time(NULL);\
    Z = difftime(Y, V);\
};

Live code demonstration

In case, the function returns a void , such functions can be timed thus:

#define timefnvoid(X, Z){\
    time_t V = time(NULL);\
    X;\
    time_t Y = time(NULL);\
    Z = difftime(Y, V);\
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM