简体   繁体   English

使用 C++14 lambda 测量任意函数的执行时间

[英]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++". Scott Meyer 的书“Effective Modern C++”中的第 24 条让我很兴奋。 He mentions the possibility to write a C++14 lambda to record the time taken in an arbitrary function invocation.他提到了编写 C++14 lambda 来记录任意函数调用所用时间的可能性。

I am still in an early of learning C++14 features.我仍处于学习 C++14 功能的早期阶段。 My attempt (Main.cpp) looks like this for measuring the time of a member function call:我的尝试 (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;
}

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).它必须是 lambda(或自定义类类型,但 lambda 更简单)。 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.您的measure最接近第二个变体,唯一有问题是声明。 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在 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.您的程序(但不是measure本身)的另一件事是您尝试使用成员函数的方式。

Function function = instance.computation;

This just doesn't work.这是行不通的。 Use a lambda or std::bind to create a bound member function.使用 lambda 或std::bind创建绑定成员函数。 There's about a zillion questions (and great answers) about the correct way to do it on stackoverflow.关于在 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) .特别注意decltype(auto)的宗教用途。 Also mutable in the second version.在第二个版本中也是mutable的。

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:如果函数返回void ,则可以这样对此类函数进行计时:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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