C++ C++;使用std::chrono可以很好地度量成员函数的执行情况

C++ C++;使用std::chrono可以很好地度量成员函数的执行情况,c++,time,std,chrono,C++,Time,Std,Chrono,我想优化我的应用程序,特别是某些函数的执行速度 假设有一个包含一些成员函数的类 class Test { public: Test(); virtual ~Test(); int init(int arg1, double arg2); private: [...] 在我的构造函数中,我调用其中一个方法 Test::Test() { [...] int value = init(1, 1.2); } 如何在不中断程序的情况下,以一种简洁明了的

我想优化我的应用程序,特别是某些函数的执行速度

假设有一个包含一些成员函数的类

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";
}
我不知道是否可以将返回值从
function()
传递到
countTime()
,以避免中断代码的工作流程

编辑: 这是我的计时课

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)...);
    }
}
int init(const std::string&filepath)const
在这里将字符串带到文件中。所以在我的情况下,这只是一个论点

不幸的是,我得到了一个非静态成员函数“int init(const string&)const”的
无效用法
错误

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

编辑2:

根据OznOg的回答,我只是忘了交一个指向我函数的指针

因此,这将是正确的函数调用

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

您可以创建一个类,如:

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;
};
依我看,这比你提议的要少一些

如果确实需要函数,可以尝试以下包装器:

template <class T, class F, class... Args>
auto MeasureTimeFn(T *t, F &&fn, Args&&... args) {
    MeasureTime timer;
     return (t->*fn)(std::forward<Args>(args)...);
}
但不确定它是否真的好得多

您可以尝试使用宏隐藏对象:

#define MEASURE(f, ...) \
  MeasureTimeFn(this, &std::remove_reference_t<decltype(*this)>::f, __VA_ARGS__)
这与您所要求的非常相似,但只在成员函数内部工作,成员函数(非静态)

无论如何,这可能是一个好的开始

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

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);
}
模板
结构测量醇{
模板
自动测量(F&&fn,参数&&…参数){
工具:TimeMeasure TimeMeasure;
返回(静态_cast(this)->*fn)(标准::向前(args)…);
}
};
类别测试:公共测量工具
{
公众:
Test();
虚拟~Test(){}
int init(const std::string&filepath){u path=filepath;返回0;}
const auto&getPath()const{return\u path;}
私人:
std::string\u路径;
};
Test::Test()
{
std::string文件路径(“/some/where”);
int value=measure(&Test::init,filepath);
度量(&Test::getPath);
}
而且,这一次,似乎符合您的第一个要求(但非常麻烦…)


现在,一切都在您的手中:)

您可以使用这样的实现:

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);
}
实时版本可在处获得。

我始终使用

#包括
...
boost::timer::cpu\u定时器;
对于(int i=0;i<1000;++i)
funct();

std::无法将
停止-启动
分配到
std::chrono::duration
,并删除
*1000
。让
为您进行这些转换。如果你养成这样做的习惯,当事情变得非常复杂时,你会有更少的编码错误。@HowardHinnant谢谢你的建议!我不知道this@LightnessRacesinOrbit我已经发现了这个工具。不幸的是,这对我的需要来说太多了。我只想测量某些函数。此外,
gprof
似乎会产生相对较大的开销。它不是“太多”,它正是分析函数执行的合适工具,而不是通过应用程序散布时间度量值,因为应用程序容易出现各种错误。当然,它会减慢你的程序速度,所以你不会得到绝对速度,但绝对速度无论如何都是无用的(你的程序将在多台电脑上运行,对吗?)-你需要知道哪个函数当前比所有其他函数都慢,这一点是正确的。目前我也用类似的方式来做。但是如果我让析构函数为我做这项工作,并将
int value=init(…)
放入作用域,我将丢失
value
。我返回某些内容并将其保存在变量中是有原因的。我想稍后在我的
Test()
构造函数中对它做些什么。我认为在我高效的codewith语句表达式中尽可能少地更改会很好,您可以删除模板函数,但不确定这是否更好(可移植性较差),我喜欢更新的方式。我认为,如果您只想测量函数的时间,而不经常更改代码,那么就不那么容易混淆了。不幸的是,我在编译时遇到了一个
无效使用非静态成员函数“[…]”
的错误。我认为指向我的成员函数的指针应该足够调用它了?你应该在某处显示代码,我认为你不是从成员函数调用宏;宏还可以,但没有直接使用,缺少了&;您应该尝试使用
int value=MeasureTimeFn(this,&Test::init,1,1.2)
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);
}
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);
}
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;
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;
#include <boost/timer/timer.hpp>

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

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

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