【问题标题】:C++ use std::chrono to measure execution of member functions in a nice wayC++ 使用 std::chrono 以一种很好的方式测量成员函数的执行
【发布时间】:2018-10-20 12:58:32
【问题描述】:

我想优化我的应用程序,尤其是某些功能的执行速度。

想象有一个类有一些成员函数

class Test
{
public:
    Test();
    virtual ~Test();
    int init(int arg1, double arg2);

private:
    [...]

在我的构造函数中,我调用了这些方法之一

Test::Test()
{
    [...]
    int value = init(1, 1.2);
}

如何在不破坏程序的情况下以简洁明了的方式测量方法 init(...) 的执行时间?

目前我使用以下代码

Test::Test()
{
    [...]
    auto start = std::chrono::high_resolution_clock::now();

    int value = init(1, 1.2);

    auto stop = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> duration = stop - start;
    std::cout << duration.count() * 1000 << "ms\n";
}

它按预期工作,但我认为它非常混乱,我想要一个“更清洁”的解决方案。

有没有办法让某种函数接受一个成员函数和其他类似的参数

int value = countTime(function, arg1, arg2);

我不知道是否可以将返回值从function() 传递给countTime(),以免中断我的代码的工作流程。

编辑: 这是我的 TimeMeasure 课程

namespace tools 
{
    class TimeMeasure 
    {
    public:
        TimeMeasure() 
        {
            m_start = std::chrono::high_resolution_clock::now();
        }

        virtual ~TimeMeasure()
        {
            m_stop = std::chrono::high_resolution_clock::now();
            std::chrono::duration<double, std::milli> duration = m_stop - m_start;
            std::cout << duration.count() << "ms\n";
        }

    public:
        typedef std::chrono::time_point<std::chrono::high_resolution_clock> HighResClock;

    private:
        HighResClock m_start;
        HighResClock m_stop;
    };

    template <typename T, typename F, typename... Args>
    auto measure(T *t, F &&fn, Args... args)
    {
        tools::TimeMeasure timeMeasure;
        return (t->*fn)(std::forward<Args>(args)...);
    }
}

在我的构造函数Test() 中,我以这种方式使用函数measure

Test()
{
    [...]
    tools::measure(this, Test::init, filepath);
}

int init(const std::string&amp; filepath) const 在此处将字符串写入文件。所以就我而言,这只是一个论点

不幸的是,我收到了 invalid use of non-static member function 'int init(const string&amp;) const' 错误

我想知道构造函数是否不是成员函数。那么为什么会出现这个错误呢?

编辑 2:

根据 OznOg 的回答,我只是忘记提交指向我的函数的指针。

所以这将是正确的函数调用

tools::measure(this, &Test::init, filepath);

【问题讨论】:

  • 将stop - start 分配给std::chrono::duration&lt;double, std::milli&gt;,然后删除* 1000。让&lt;chrono&gt; 为您进行这些转换。如果您养成这样做的习惯,当事情变得真正复杂时,您的编码错误就会减少。
  • @HowardHinnant 感谢您的建议!我不知道这个
  • @LightnessRacesinOrbit 我已经发现了这个工具。不幸的是,这对我的需求来说有点太多了。我只想测量某些功能。此外,gprof 似乎产生了比较大的开销。
  • 这不是“太多”,它正是分析函数执行的合适工具,而不是通过容易出现各种错误的应用程序进行时间测量。当然,它会减慢您的程序速度,因此您不会获得绝对速度,但无论如何绝对速度是无用的(您的程序将在多台 PC 上运行,对吗?) - 您需要知道当前哪个功能比所有其他功能,为此它很到位。

标签: c++ time std chrono


【解决方案1】:

你可以像这样创建一个类:

struct MeasureTime {
    MeasureTime() : _start(std::chrono::high_resolution_clock::now()) {}

    ~MeasureTime() {
        auto stop = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double> duration = stop - _start;
        std::cout << duration.count() * 1000 << "ms\n";
    }
private:
    std::chrono::time_point<std::chrono::high_resolution_clock>  _start;
};

只需在您的代码中使用它:

Test::Test()
{
    MeasureTime mt;
    [...]
    { //or even this for just the init call
    MeasureTime mt2;
    int value = init(1, 1.2);
    }
}

恕我直言,它比您建议的要少。

如果你真的想要一个函数,你可以试试这样的包装器:

template <class T, class F, class... Args>
auto MeasureTimeFn(T *t, F &&fn, Args&&... args) {
    MeasureTime timer;
     return (t->*fn)(std::forward<Args>(args)...);
}

然后这样称呼它:

int value = MeasureTimeFn(this, &Test::init, 1, 1.2);

但不确定它是否真的好多了。

你可以尝试用宏来隐藏东西:

#define MEASURE(f, ...) \
  MeasureTimeFn(this, &std::remove_reference_t<decltype(*this)>::f, __VA_ARGS__)

这样你就可以写了

int value = MEASURE(init, 1, 1.2);

与您要求的非常相似,但仅适用于成员函数,具有成员函数(非静态)。

无论如何,这可能是一个不错的起点。

* 编辑* 如果你可以修改你的类的继承,你可以试试

template<class T>
struct MeasureTool {
    template <class F, class... Args>
    auto measure(F &&fn, Args&&... args) {
        tools::TimeMeasure timeMeasure;
        return (static_cast<T*>(this)->*fn)(std::forward<Args>(args)...);
    }
};

class Test : public MeasureTool<Test>
{
public:
    Test();
    virtual ~Test() {}
    int init(const std::string &filepath) { _path = filepath; return 0; }
    const auto &getPath() const { return _path; }
private:
    std::string _path;

};

Test::Test()
{
    std::string filepath("/some/where");
    int value = measure(&Test::init, filepath);
    measure(&Test::getPath);
}

而且,这一次,似乎符合您的第一个要求(但相当麻烦...)

现在,一切尽在你手中 :)

【讨论】:

  • 目前我以类似的方式进行操作。但是,如果我让析构函数为我完成工作并将int value = init(...) 放入作用域,我将失去value。我返回一些东西并将其保存在变量中是有原因的。我想稍后在我的Test() 构造函数中对其进行处理。我认为在我的生产代码中尽可能少地改变会很好
  • 带语句表达式,可以去掉模板函数,不知道这样会不会更好(移植性较差)
  • 我喜欢更新的方式。我认为如果您只想测量功能时间而不是大量更改代码,那么它就不那么令人困惑了不幸的是,我在编译时遇到了invalid use of non-static member function ‘[...]' 错误。我认为指向我的成员函数的指针应该足以调用它?
  • 您应该在某处显示代码,我认为您不是从成员函数调用宏。
  • 好的,有一个错字;宏没问题,但不是直接使用缺少 & ;你应该试试int value = MeasureTimeFn(this, &amp;Test::init, 1, 1.2);
【解决方案2】:

我总是使用Boost.Timer。

#include <boost/timer/timer.hpp>

...
boost::timer::cpu_timer timer;

for(int i = 0; i < 1000; ++i)
  funct();

std::cout << timer.format();

它显示了墙、用户和系统时间。

【讨论】:

    【解决方案3】:

    你可以使用这样的实现:

    template<typename Fn, typename... Args>
    typename std::enable_if<!std::is_void<typename std::result_of<Fn&&(Args...)>::type>::value, 
        std::tuple<std::chrono::duration<double>, typename std::result_of<Fn&&(Args...)>::type>
    >::type
    countTime(Fn&& fn, Args&&... args) {
        auto start = std::chrono::high_resolution_clock::now();
        auto fnret = std::forward<Fn>(fn)(std::forward<Args>(args)...);
        auto stop = std::chrono::high_resolution_clock::now();
        return std::make_tuple(stop - start, fnret);
    }
    
    template<typename Fn, typename... Args>
    typename std::enable_if<std::is_void<typename std::result_of<Fn&&(Args...)>::type>::value, 
        std::tuple<std::chrono::duration<double>>
    >::type
    countTime(Fn&& fn, Args&&... args) {
        auto start = std::chrono::high_resolution_clock::now();
        std::forward<Fn>(fn)(std::forward<Args>(args)...);
        auto stop = std::chrono::high_resolution_clock::now();
        return std::make_tuple(stop - start);
    }
    
    template<typename R, class C, typename... Args>
    typename std::enable_if<!std::is_same<R, void>::value,
        std::tuple<std::chrono::duration<double>, R>
    >::type
    countTime(R (C::*fn)(Args...), C& obj, Args&&... args) {
        auto start = std::chrono::high_resolution_clock::now();
        auto fnret = (obj.*fn)(std::forward<Args>(args)...);
        auto stop = std::chrono::high_resolution_clock::now();
        return std::make_tuple(stop - start, fnret);
    }
    
    template<class C, typename... Args>
    std::tuple<std::chrono::duration<double>>
    countTime(void (C::*fn)(Args...), C& obj, Args&&... args) {
        auto start = std::chrono::high_resolution_clock::now();
        (obj.*fn)(std::forward<Args>(args)...);
        auto stop = std::chrono::high_resolution_clock::now();
        return std::make_tuple(stop - start);
    }
    

    这将在 void (*)(...) 函数的情况下返回一个具有一个成员持续时间的元组,在函数返回 smth 的情况下返回一个具有持续时间和返回类型的元组。它适用于 std::bind 和成员函数。可能重载可能会合并到一两个实现中,但我不知道该怎么做。

    调用示例:

    auto ret = countTime([](int a) -> int { 
        std::this_thread::sleep_for(std::chrono::milliseconds(a)); 
        return a * 2;
    }, 10);
    std::cout << "function executed in: " <<
        std::chrono::duration_cast<std::chrono::milliseconds>(std::get<0>(ret)).count() <<
        " milliseconds." << std::endl;
    std::cout << "function returned: " << std::get<1>(ret) << std::endl;
    

    更简单的示例调用:

    auto ret = countTime(&Test::init, *this, 1, 1.2);
    int value = std::get<1>(ret);
    
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(std::get<0>(ret)).count() << "ms" << std::endl;
    

    简单的简单示例调用,假设int init(int, int)函数:

    auto ret = countTime(init, 1, 1.2);
    int value = std::get<1>(ret);
    
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(std::get<0>(ret)).count() << "ms" << std::endl;
    

    onlinegdb 提供实时版本。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-16
      • 1970-01-01
      • 1970-01-01
      • 2017-08-31
      • 2011-01-27
      • 2016-04-28
      相关资源
      最近更新 更多