It's often useful to be able to time code, for instance, to evaluate alternative approaches to the same problem. Because this is a thing I use frequently, I have created this Stopwatch templated class that I'd like for you all to review. Unlike some other recent code, this class is intended to be cross-platform and relies only on a working implementation of the std::chrono
utilities in C++11.
Specifically, does the interface make sense? Could the code be improved? Also, I have thought it might be nice to provide some kind of a "units" function to return, say, a std::string
containing "ms" if TimeT
is set to std::chrono::milliseconds
but ultimately decided against complicating the interface. I'd be open to comments on that decision as well.
stopwatch.h
#ifndef STOPWATCH_H
#define STOPWATCH_H
#include <chrono>
template<typename TimeT = std::chrono::microseconds,
typename ClockT=std::chrono::high_resolution_clock,
typename DurationT=double>
class Stopwatch
{
private:
std::chrono::time_point<ClockT> _start, _end;
public:
Stopwatch();
void start();
DurationT restart();
DurationT stop();
DurationT elapsed() const;
};
template<typename TimeT, typename ClockT, typename DurationT>
Stopwatch<TimeT, ClockT, DurationT>::Stopwatch()
{
start();
}
template<typename TimeT, typename ClockT, typename DurationT>
void Stopwatch<TimeT, ClockT, DurationT>::start()
{
_start = _end = ClockT::now();
}
template<typename TimeT, typename ClockT, typename DurationT>
DurationT Stopwatch<TimeT, ClockT, DurationT>::stop()
{
_end = ClockT::now();
return elapsed();
}
template<typename TimeT, typename ClockT, typename DurationT>
DurationT Stopwatch<TimeT, ClockT, DurationT>::restart()
{
DurationT ret = stop();
start();
return ret;
}
template<typename TimeT, typename ClockT, typename DurationT>
DurationT Stopwatch<TimeT, ClockT, DurationT>::elapsed() const
{
auto delta = std::chrono::duration_cast<TimeT>(_end-_start);
return delta.count();
}
#endif
stoptest.cpp
#include <iostream>
#include <cmath>
#include "stopwatch.h"
int main()
{
const int iterations = 1000000;
double sqrtsum = 0;
Stopwatch<> sw;
for (int i=0; i<iterations; ++i)
sqrtsum += sqrt(i);
std::cout << "calculated " << sqrtsum << " in " << sw.restart() << " us\n";
sqrtsum = 0;
for (int i=0; i<iterations; ++i)
sqrtsum += pow(i,0.5);
std::cout << "calculated " << sqrtsum << " in " << sw.stop() << " us\n";
}
A few words about usage
The goal was to create a minimal but complete interface. The constructor does nothing except calling start
.
start
sets both starting and ending times to the current underlying clock's now()
and essentially functions as a reset (which was another name I considered for this function.)
elapsed
does not modify the underlying Stopwatch
and only returns the difference between the internally stored _end
and _start
.
stop
returns the elapsed time between "now" and the internally stored _start
and so can be used to measure several times all relative to the same start time.
restart
does the same thing as stop
except it also resets the start time.