Search code examples
c++templatesvariadic-templatesc++-chrono

Generic duration meter for any operator, function call and constructor


I was using a templatized meter function (see below) to measure the elapsed time for functions. Then I also wanted to use it for constructors.

As far as I know there is no way of passing a type as a function parameter directly. So I came up with this workaround, passing it solely as the template parameter (minimal example):

template <typename T, typename ... P>
auto meter(T t, P ... p) {
    auto t1 = high_resolution_clock::now();
    t(p...);
    auto t2 = high_resolution_clock::now();
    auto dif = t2-t1;   
    return duration_cast<microseconds>(dif);
}

template <typename T, typename ... P>
auto meter(P ... p) {
    auto t1 = high_resolution_clock::now();
    auto t = T(p...);
    auto t2 = high_resolution_clock::now();
    auto dif = t2-t1;   
    return duration_cast<microseconds>(dif);
}

int main() {
    auto d = meter(g, 1.0, 20.0); //meter the function call g(1.0, 20.0)
    std::cout << "Ellapsed time: " << d.count() << " microseconds\n";
    d = meter(complex_obj{2}); //meter () operator of complex_obj, assuming complex_obj{int} is trivial;
    std::cout << "Ellapsed time: " << d.count() << " microseconds\n";
    d = meter<complex_obj>(); //meter constructor complex_obj();
    std::cout << "Ellapsed time: " << d.count() << " microseconds\n";
}

Trying this got me thinking. Is there a general/consistent way of rewriting this, as to apply to any kind of computation (not just constructor, but maybe even other operators like (obj1 < obj2)? I noticed, that I was already (accidentally) supporting the () operator of structs.

Sorry if this question became to broad, my main question is, if there is a way to unite the syntax of meter call, for functions and constructors alike.


Solution

  • You can wrap the code to be measured inside a lambda (since C++11):

    #include <chrono>
    #include <iostream>
    
    template<class F>
    auto meter(F&& f) {
      auto t1 = std::chrono::high_resolution_clock::now();
      f();//                                                <-- operator() of the lambda
      auto t2 = std::chrono::high_resolution_clock::now();
      auto dif = t2-t1;
      return std::chrono::duration_cast<std::chrono::microseconds>(dif);
    }
    
    void g(double x, double y) {
      std::cout << "g(" << x << ", " << y << ")\n";
    }
    
    int main() {
      double x = 1.0;
      auto d = meter([&] {
        // This comment is inside the *body* of the lambda.
        // Code of the {body} is executed upon `operator()`.
        g(x, 20.0);// note that you can use `x` here thanks to the capture-default `[&]`
      });
      std::cout << "time: " << d.count() << " ms\n";
    }