测量函数的执行时间

时间:2020-08-27 10:37:52

标签: c++ time c++14 c++17

我想测量函数执行的时间。

我可以这样使用:

template <typename Function, typename... Args>
auto measure_time(Function func, Args&&... args)
{
    using namespace std::chrono;
    auto start = steady_clock::now();
    func(std::forward<Args>(args)...);   // handle lvalues and rvalues
    return duration<double>(steady_clock::now() - start).count();
}

但是对我来说,我似乎非DRY。所以我创建了一个小函数来做到这一点:

measure_time(func, arg1, arg2, arg3);       // for common functions
measure_time([](){func(arg1,arg2, arg3););  // for member or template functions

我这样称呼它:

func

这对我来说很好,但确实有一些缺点:

  1. 我不清楚如何方便地 更改它以同时获取void(当然也可以是important_function(arg1, arg2); // reads well measure_time(important_function, arg1, arg2); // measure_time steals the spotlight )的返回值?< / li>
  2. 这显然与功能单一的规则相矛盾
  3. 代码的可读性被大大破坏:
    using namespace std::chrono;
    template <typename Container>
    class Timer{
       public:
        Timer(Container& _c) : c(_c) {}
        ~Timer() {
            c.push_back(duration<float>(steady_clock::now() - start).count());
        }
       private:
        time_point<steady_clock> start{steady_clock::now()};
        Container& c;
    };
    

是否有应对这些问题的准则?


更新:

我忘了提到函数执行后,我需要将经过的时间存储到容器中。


更新2:

在@puio回答之后,我最终得到了这一点:

auto mc = MyContainer{};
...
{
    auto t = Timer<MyContainer>(mc);
    // things to measure
}
// mc.back() is the elapsed time

用法:

v-pre

干燥并清洁:)

3 个答案:

答案 0 :(得分:2)

在函数中实例化此类,它将显示对象超出范围的时间。

class Timer {
 private:
  std::chrono::steady_clock::time_point begin;
  std::string_view func_name;

 public:
  Timer(std::string_view func)
  {
    func_name = func;
    begin = std::chrono::steady_clock::now();
  }
  ~Timer()
  {
    const std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
    std::cout << func_name << " Time: "
              << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count()
              << " microseconds " << std::endl;
  }
};

用法:

void slow_func(){
  Timer timer{__func__};
  // ...
  // Destruct automatically.
}

chrono函数是从这里获取的,因为我一直忘了它。


但是我需要将经过的时间存储在一个容器中

我想象它是std::vector,通过非常量引用(作为返回值)将其传递给构造函数,将该引用存储在private访问中,并且push_back析构函数中的向量。

这样做的公共成员函数,或者用所说的std::vector显式调用析构函数,只会很麻烦。此外,呼叫者可能会错过使用该功能的机会。

答案 1 :(得分:1)

如果记录时间不属于程序的主要目标。您可以使用GCC -finstrument-functions选项并提供功能的定义:

struct Record {
   std::chrono::time_point<std::chrono::steady_clock> start, stop;
};
std::map<void *, Record> container;

void __cyg_profile_func_enter (void *this_fn,
                               void *call_site) {
    auto &record = container[this_fn];
    auto start = std::chrono::steady_clock::now();
    // do something with start
    record.start = start;
}

void __cyg_profile_func_exit  (void *this_fn,
                               void *call_site) {
    auto stop = std::chrono::steady_clock::now();
    // do something with stop
    container[this_fn].stop = stop;
}

答案 2 :(得分:1)

...我尚不清楚如何方便地更改它以同时检索func

的返回值。

另一种可能的解决方案是返回持续时间的tuple,如果不是void,则返回函数的返回值。

类似这样的东西(C ++ 17):

using Clock = std::chrono::high_resolution_clock;

template <typename Function, typename... Args>
auto measure_time(const Function& function, Args&&... args) {
    auto start = Clock::now();

    if constexpr (std::is_same_v<std::invoke_result_t<Function, Args...>, void>) {
        std::invoke(function, std::forward<Args>(args)...);
        return std::make_tuple<Clock::duration>(Clock::now() - start);
    }
    else {
        std::tuple<Clock::duration, std::invoke_result_t<Function, Args...>> res{0, std::invoke(function, std::forward<Args>(args)...) };
        std::get<0>(res) = Clock::now() - start;
        return res;
    }
}

用作:

using namespace std::chrono_literals;

int main() {
    auto [dur, res] = measure_time(&some_func, some_args);
    std::cout << "some_func: " << res << ", time: " << dur / 1us << "us\n";
}

注意:

  • 通过创建元组和调用结果,我们将从RVO中受益(结果将存储在调用站点中,而不会被复制/移动)。

  • std::invoke允许调用指针成员函数。