I want to make a benchmark function that, given an invocable and its parameters, will time the execution of the given invocable and return the measured std::chrono::duration<...> as well as the value returned by the invocation of the invocable.
I am having problems with perfect forwarding the value returned from the invocation. Currently I return the value returned by the invocation and use a reference parameter to return the duration:
using bench_clock = std::conditional_t<std::chrono::high_resolution_clock::is_steady,
std::chrono::high_resolution_clock, std::chrono::steady_clock>;
decltype(auto) benchmark(
bench_clock::duration& duration, auto&& func, auto&&... args)
{
auto begin{ bench_clock::now() };
decltype(auto) result{
std::invoke(
std::forward<decltype(func)>(func),
std::forward<decltype(args)>(args)...)
};
duration = bench_clock::now() - begin;
return result;
}
As far as I know, this perfectly forwards the value returned from the invocation.
I would prefer to also return the duration conventionally, as an example, by using std::tuple though I am not sure how to do it t perfectly forward the returned value.
My guess would be to use std::invoke_result_t like this:
using bench_clock = std::conditional_t<
std::chrono::high_resolution_clock::is_steady,
std::chrono::high_resolution_clock, std::chrono::steady_clock>;
auto benchmark(auto&& func, auto&&... args)
{
auto begin{ bench_clock::now() };
decltype(auto) result{
std::invoke(
std::forward<decltype(func)>(func),
std::forward<decltype(args)>(args)...)
};
auto duration{ bench_clock::now() - begin };
return std::tuple<std::invoke_result_t<decltype(func), decltype(args)...>, bench_clock::duration>{std::forward<decltype(result)>(result), duration};
//------------------------------------------------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ is this needed?
}
I am not sure if this approach correctly perfectly forwards. I also don't know if is required to use std::forward in the std::tuple constructor.
There is also a problem, that if the invocable returns void, the tuple cannot be used, as std::tuple<void> cannot be instantiated.
I am not sure how to go around this.