Stelios Daveas
Stelios Daveas

Reputation: 23

Measure the execution time of a function

I want to measure the time of a function execution.

I could use something like this:

using namespace std::chrono;
auto start = steady_clock::now();
// process
auto end = duration<double>(steady_clock::now() - start).count();

but I it seems pretty non-DRY to me. So I created a small function to do it:

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();
}

and I call it as such:

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

This works fine for me, but it does come with some drawbacks:

  1. it is not clear to me how it be conveniently changed to also retrieve the return value of func (which, of course, can also be void)?
  2. this clearly contradicts with the one-thing-per-function rule
  3. the readability of the code is significantly undermined:
    important_function(arg1, arg2);                // reads well
    measure_time(important_function, arg1, arg2);  // measure_time steals the spotlight 
    

Are there any guidelines to face these issues?


Update:

I forgot to mention that after the execution of the function I need to store the time elapsed to a container.


Update 2:

after @puio 's answer, I ended up with this:

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;
};

Usage:

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

DRY and clean :)

Upvotes: 2

Views: 863

Answers (3)

rustyx
rustyx

Reputation: 85266

... it is not clear to me how it be conveniently changed to also retrieve the return value of func

Another possible solution is to return a tuple of the duration and (optionally) the function's return value, when it's not void.

Something like this (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;
    }
}

Use as:

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";
}

Notes:

  • By creating the tuple together with the invoke result, we benefit from RVO (the result will be stored at the call site, never copied/moved).

  • std::invoke allows invoking pointer-to-member functions.

Upvotes: 1

Jorge Bellon
Jorge Bellon

Reputation: 3096

If recording the time is not part of the main goal of your program. You can use GCC -finstrument-functions option and provide a definition for the 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;
}

Upvotes: 1

puio
puio

Reputation: 1278

Instantiate this class in a function and it will print the time when the object goes out of scope.

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;
  }
};

Usage:

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

The chrono functions taken from here since I keep forgetting it.


But I need to store the time elapsed in a container

I imagine it is std::vector, pass it by non-const reference (as a return value) to the Constructor, store the reference in private access, and push_back the time to the vector in the destructor.

A public member function to do so, or calling the destructor explicitly with the said std::vector just makes it cumbersome. Also, callers may miss using that function.

Upvotes: 2

Related Questions