Compose function to measure function call times
If you want to do serious measurements of you code use one of the existing
libraries for this purpose, for example
google-benchmark or
Celero.
But if you just want to do simple and maybe temporary measure parts of
your code, todays C++ with std::chrono provides some very useful helpers for
this purpose.
Measure by using std::chrono
Just take std::chrono::system_clock::now()
to get a start and an end time
point, take the difference of these (end - start), and cast it into a time
duration, maybe nanoseconds.
The example code will not much differ from that one in next section, so no need to be repetitive here.
Write a stop watch using std::chrono
While writing code like mentioned above is easy, it soon becomes tedious. Maybe already after the second time of writing it. The natural reflex of a programmer is to encapsulate this. One way would be to create a StopWatch class.
A possible implementation could look like this, of course various variations of this are possible.
class StopWatch
{
public:
void start()
{
Expects(not started) ;
started=true ;
start_time = std::chrono::system_clock::now();
}
std::chrono::nanoseconds stop()
{
using namespace std::chrono ;
auto end_time = system_clock::now();
Expects(started) ;
started = false ;
return duration_cast<nanoseconds>(end_time -start_time) ;
}
private:
decltype(std::chrono::system_clock::now()) start_time ;
bool started{false} ;
};
int main ()
{
StopWatch sw ;
sw.start() ;
std::cout << "This took now "
<< sw.stop().count()
<< " nanoseconds" <<std::endl ;
return 0 ;
}
There are of course some variations possible, like starting the StopWatch in
the constructor, and each stop
call returns the time since construction.
Such details are up to personal taste and the current use case.
The hardest problem for such implementations might be to find good names for
the classes.
Compose function to measure function call times
It is very likely that the code we want to measure is already packed into a function, and the actual point if interest is: How long does it take to call this function.
We can of course also use the StopWatch from above to simple measure the duration of the function call, and it is maybe the best solution.
But than I would have to stop the post here and chose some different title ;-)
So lets see this also a nice possibility to show how to compose functions add compile
time
and select different implementations depending on the return type of a
function.
For this purpose, timing a function call and get its return value is a good example.
- Therefore, say I have a function that
-
-
takes 0..n arguments
-
might return something or not
-
needs to be pass into an other function that returns the duration of the call plus, if there is, the return value of the other function
-
Lets call the function that returns the duration timed_call
, and something
like this should work
void print_hello(){ std::cout << "hello" << std::endl; }
template<typename T> T sum(const T& a, const T& b) { return a + b ;}
auto timed = timed_call(print_hello) ;
std::cout << "print_hello needs " << timed.duration.count() << "nanoseconds" << std::endl;
// or
auto timed = timed_call(sum<int>,1,3) ;
std::cout << "sum needs " << timed.duration.count() << "nanoseconds"
<< " and returns " << timed.return_value << std::endl ;
The return value of timed_call
need to be a duration, and if
the given function returns something, also a return value.
This could be a tuple with one or 2 elements, or a struct.
I prefer the struct since it allows me to name the elements what produces imho
better readable code.
And I call this struct TimedCallResult
for now.
To be able to model the different return types I need, either just a duration or a duration with some value, tepmlate specialization can be used.
template<typename ResultType>
struct TimedCallResult
{
std::chrono::nanoseconds duration ;
ResultType return_value;
};
template<>
struct TimedCallResult<void>
{
std::chrono::nanoseconds duration ;
};
To get the TimedCallResult
from a timed_call
some glue is needed to select the
correct result type.
The glue from timed_call to select and create the TimedCallResult. Buy doing this, the actual measurement can also be done.
The glue code takes one additional template parameter, what is the return type
of the function to be called.
It is implemented as a functor, or
functor, or function object,
with
partial specialization
for the void
(not jet but soon) type.
This is not pretty and therefore 'hidden' in a detail namespace. timed_call will later do the work and select and call one of these for us.
namespace details
{
template<typename ResultType, typename Invokable, typename... Args>
struct
TimedCall
{
auto operator()(Invokable f, Args&&... args)
->TimedCallResult<ResultType>
{
using namespace std::chrono ;
auto start = system_clock::now();
TimedCallResult<ResultType> r{ nanoseconds{0},
f(std::forward<Args>(args)...)} ;
r.duration = duration_cast<nanoseconds>(system_clock::now() - start);
return r ;
}
};
template<typename Invokable, typename... Args>
struct
TimedCall<void,Invokable, Args...>
{
auto operator()(Invokable f, Args&&... args)
->TimedCallResult<void>
{
using namespace std::chrono ;
auto start = system_clock::now();
f(std::forward<Args>(args)...) ;
return {duration_cast<nanoseconds>(system_clock::now() - start)} ;
}
};
}
There are fore sure different - and maybe more elegant - ways to solve this but this works and is straight forward.
The timed_call function using just the glue in details to get a TimedCallResult
template<typename Invokable, typename... Args>
auto
timed_call(Invokable f, Args&&... args)
-> TimedCallResult<decltype(f(std::forward<Args>(args)...))>
{
using ResultType = decltype(f(std::forward<Args>(args)...));
details::TimedCall<ResultType ,Invokable, Args...> meassure;
return meassure(f, std::forward<Args>(args)...) ;
}
Not a lot to do, just get the return type of the function call and forward it to the implementation details (call the glue in detail).
- All the code to play around with here
- Challenge for the reader
-
I think that C++17 will make the code above much shorter, a static_if should allow to get rid of all the glue code and have just TimedCallResult and timed_call.
Write a StopWatch than make sense to use in the detail::TimeCall glue code
The pleasant anticipation that C++17 will reduce the amount of glue code, beside of showing
some functional compositions, is the main reason for writing this blogpost.
But I would also like to mention that having such simple measurement utilities,
and some advanced ones, can reduce your
OBP dramatically.
Thanks for reading
As usual, if you find any mistakes, in the code or in my spelling, please let
me know.
Ideas for improvements, or any other feedback, is also very much welcome.