如何为每个类的方法添加计时器?

时间:2016-12-14 13:53:14

标签: c++ boost timer

如何隐含地'为每个类的方法添加一些计时器,不包括构造函数和析构函数?

我现在为每个班级方法做的事情:

void MyClass::SomeFunc()
{
    cout << __PRETTY_FUNCTION__ <<endl;

    boost::timer::cpu_timer timer;

    //Some code

    boost::timer::cpu_times elapsed = timer.elapsed();
    cout << __PRETTY_FUNCTION__ << " : WALLCLOCK TIME: " << elapsed.wall / 1e9 << " seconds" << endl;
}

我想要的是什么:

void MyClass::SomeFunc()
{
    //Some code
}

假设这两部分代码的行为应该是等效的。

4 个答案:

答案 0 :(得分:5)

使用RAII几乎可以达到这个目的:

struct FunctionLogger {
    FunctionLogger(const char* func)
        : m_func(func)
    {
        cout << func <<endl;
    }
    ~FunctionLogger() {
        boost::timer::cpu_times elapsed = timer.elapsed();
        GSULOG << m_func << " : WALLCLOCK TIME: " << elapsed.wall / 1e9 << " seconds" << endl;
    }
    const char* m_func;
    boost::timer::cpu_timer timer;
};

现在:

void MyClass::SomeFunc()
{
    FunctionLogger _(__PRETTY_FUNCTION__);
    //Some code
}

当然,如果你喜欢宏:

#define FL FunctionLogger _(__PRETTY_FUNCTION__)

void MyClass::SomeFunc()
{
    FL;
    //Some code
}

如果您正在为这类事物寻找工业级解决方案,那么艺术术语是Aspect Oriented Programming。但它不是C ++直接支持的。

答案 1 :(得分:4)

您要做的事情被称为分析(获取每个函数调用的持续时间)和检测(将代码注入函数以获取更详细但可能不太准确的时序信息)。

到目前为止这样做的最佳方式是不要自己动手,而是在分析器下运行代码(一种现成的应用程序,它可以执行时间安排,也可以选择进行检测所有这些都不会污染您的源代码。)

答案 2 :(得分:2)

如果您想避免修改代码,并愿意牺牲__PRETTY_FUNCTION__输出。您可以通过计时句柄访问该类来实现此目的。

首先你定义一个RAII类用于计时,类似于 John Zwinck 的答案:

template<typename T>
struct TimingDecorator {
  T *ptr_;
  boost::timer::cpu_timer timer;

  TimingDecorator (T* ptr) : ptr_(ptr) {}
  ~TimingDecorator () {
        boost::timer::cpu_times elapsed = timer.elapsed();
        GSULOG << " : WALLCLOCK TIME: " << elapsed.wall / 1e9 << " seconds" << endl;
  }
  T* operator->() { return ptr_; }
  T const * operator->() const { return ptr_; }
};

然后定义一个句柄,强制通过装饰器对类进行所有访问:

template<typename T>
struct TimingHandle {
  T &obj_;
  boost::timer::cpu_timer timer;

  TimingHandle (T const& obj) : obj_(obj) {}

  TimingDecorator<T> operator->() { return &obj_; }
  TimingDecorator<T const> operator->() const { return &obj_; }
};

然后,对于计时,您可以通过句柄进行所有访问:

MyClass obj;
TimingHandle<MyClass> obj_timing(obj);

GSULOG << "MyClass::SomeFunc" << endl;
obj_timing->SomeFunc();

我应该指出,最后两行可以包装在一个宏中(如果你不介意使用它),以避免重复自己。

#define MYCLASS_TIME_FUNC(handle, func) \
  GSULOG << "MyClass::" #func << endl;  \
  (handle)->func

你最终可以用作

MYCLASS_TIME_FUNC(obj_timing, SomeFunc2)(/* params for SomeFunc2 */);

答案 3 :(得分:1)

您可以使用反转计划:

template <typename Caption, typename F>
auto timed(Caption const& task, F&& f) {
    return [f=std::forward<F>(f), task](auto&&... args) {
        using namespace std::chrono;

        struct measure {
            high_resolution_clock::time_point start;
            Caption task;
            ~measure() { GSU_LOCK << " -- (" << task << " completed in " << duration_cast<microseconds>(high_resolution_clock::now() - start).count() << "µs)\n"; }
        } timing { high_resolution_clock::now(), task };

        return f(std::forward<decltype(args)>(args)...);
    };
}

您可以使用:Live On Coliru

timed_rand = time("Generate a random number", &::rand);

for (int i = 0; i<10; ++i)
     std::cout << timed_rand() << " ";

通过一些MACRO帮助,你可以使它更具有使用性:

<强> Live On Coliru

#include <iostream>
#include <chrono>
using namespace std::literals::string_literals;
#define GSU_LOG std::clog

template <typename Caption, typename F>
auto timed(Caption const& task, F&& f) {
    return [f=std::forward<F>(f), task](auto&&... args) -> decltype(auto) {
        using namespace std::chrono;

        struct measure {
            high_resolution_clock::time_point start;
            Caption task;
            ~measure() { GSU_LOG << " -- (" << task << " completed in " << duration_cast<microseconds>(high_resolution_clock::now() - start).count() << "µs)\n"; }
        } timing { high_resolution_clock::now(), task };

        return f(std::forward<decltype(args)>(args)...);
    };
}

#define TIMED(expr) (timed(__FILE__ + (":" + std::to_string(__LINE__)) + " " #expr, [&]() -> decltype(auto) {return (expr);})())

int main() {
    std::string line;
    while (TIMED(std::getline(std::cin, line))) {
        std::cout << "Simple arithmetic: " << TIMED(42 * TIMED(line.length())) << "\n";
    }
}

打印

$ clang++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp
$ for a in x xx xxx; do sleep 0.5; echo "$a"; done | ./a.out

 -- (main.cpp:25 std::getline(std::cin, line) completed in 497455µs)
 -- (main.cpp:26 line.length() completed in 36µs)
 -- (main.cpp:26 42 * TIMED(line.length()) completed in 106µs)
Simple arithmetic: 42
 -- (main.cpp:25 std::getline(std::cin, line) completed in 503516µs)
 -- (main.cpp:26 line.length() completed in 14µs)
 -- (main.cpp:26 42 * TIMED(line.length()) completed in 42µs)
Simple arithmetic: 84
 -- (main.cpp:25 std::getline(std::cin, line) completed in 508554µs)
 -- (main.cpp:26 line.length() completed in 14µs)
 -- (main.cpp:26 42 * TIMED(line.length()) completed in 38µs)
Simple arithmetic: 126
 -- (main.cpp:25 std::getline(std::cin, line) completed in 286µs)

请注意,您还可以使lambda累积不同呼叫的数据并报告总数/平均值。