It’s only February, and already, I’ve missed a post. I guess I’ll add that to the queue and try to post twice in one of the coming weeks.
This week I have a relatively short post. However, if it’s still too long to read, at least make sure you check our the nifty progress bar that was the result of me playing with the C++11 chrono facilities.
Anyone who knows me will know that I’m fond of progress bars for any computation that requires more than a few minutes. If there’s a process that’s going to take more than, say, 5 minutes, I find it incredibly helpful to know if I should expect to be waiting 10 minutes (and go get a cup of coffee) 100 minutes (and go to the gym) or longer (and go write my own progress bar for whatever poor application I happen to be using). For most of my previous C++ projects, I used Boost’s progress bar. Unfortunately, this reliable old tool has been deprecated, and including it can lead to conflicting declarations with some of Boost’s newer and very useful timing utilities.
After moving away from Boost’s solution, I searched around for a while and settled on ezProgressBar; a lightweight set of headers that provide pre-stylized progress bars. This library is nice, and doesn’t conflict with any of the other Boost timing utilities, but one day, after just arriving at work, I decided I needed a Monday morning project. What I mean by a Monday morning project is a small but satisfying project, the first revision of which can usually be completed in an hour or so, to help get you into a groove when you’re not really motivated to jump into a larger or more foreboding piece of code. So I decided that I’d retrofit ezProgressBar to use C++11’s new timing facilities (i.e. std::chrono). The motivation for this (besides the normal Monday morning project motivation) was to get a little bit of practice with C++11’s new date / time classes, and to get rid of some of the platform specific cruft, present in the aforementioned library, which becomes unnecessary if one can assume C++11 compatibility of the hosting compiler.
All things considered, I must say that I’m fairly impressed with the design of std::chrono. They’ve largely eliminated the nagging questions like “what unit of time does this function take?” Also, duration_cast get’s rid of the tedious work of converting between different units of time, thus helping to eliminate those annoying bugs that result from difficulty with enumerating the correct number of zeros. The standard library also makes it easy for you to define your own time durations, and to have these user defined durations fit in almost seamlessly with the built-in types. Let’s take a look at a straightforward translation of ezProgressBar’s durationString function into C++11. First, the original:
[cc lang=’cpp’ ]
std::string secondsToString(long long t) {
int days = t/86400;
long long sec = t-days*86400;
int hours = sec/3600;
sec -= hours*3600;
int mins = sec/60;
sec -= mins*60;
char tmp[8];
std::string out;
if (days) {
sprintf(tmp, “%dd “, days);
out += tmp;
}
if (hours >= 1) {
sprintf(tmp, “%dh “, hours);
out += tmp;
}
if (mins >= 1) {
sprintf(tmp, “%dm “, mins);
out += tmp;
}
if (sec >= 1) {
sprintf(tmp, “%ds”, (int)sec);
out += tmp;
}
if (out.empty())
out = “0s”;
return out;
}
[/cc]
Now, the C++11 code:
[cc lang=’cpp’ ]
std::string durationString( duration t ) {
typedef std::chrono::duration
std::stringstream out(std::stringstream::out);
//std::string out;
if ( t >= days(1) ) {
auto numDays = duration_cast
out << numDays.count() << "d ";
t -= numDays;
}
if ( t >= hours(1) ) {
auto numHours = duration_cast
out << numHours.count() << "h ";
t -= numHours;
}
if ( t >= minutes(1) ) {
auto numMins = duration_cast
out << numMins.count() << "m ";
t -= numMins;
}
if ( t >= seconds(1) ) {
auto numSecs = duration_cast
out << numSecs.count() << "s";
}
std::string tstring = out.str();
if (tstring.empty()) { tstring = "0s"; }
return tstring;
}
[/cc]
The point of showing this snippet is not the code length ... both are relatively short pieces of code and, undoubtedly, both could be made shorter. The point I want to draw attention to is how much less type and unit "fudging" is going on the second sample than the first. The first version takes a long long . . . why? What units does this number represent, second, milliseconds, something else? The second version takes a duration (of time). While the duration itself doesn't specify the units in the type declaration, it "knows" about the units. This is how the duration_cast allows us to cast our duration to an array of other units.
The second point to contrast is all of the "magic" numbers that appear in the first snippet. Now, these numbers aren't really "magic" and most people looking at the code will get where, for example, the 3600 comes from. However, if I have a duration, t, and I want to know what it means in hours, minuetes, or seconds, I can just do the following:
[cc lang="cpp"]
auto numHours = duration_cast
auto numMinutes = duration_cast
auto numSeconds = duration_cast
[/cc]
I don’t have to know what unit t is originally in terms of, and this makes extracting the information I want much clearer and less error prone. Also, defining your own duration (and having it act like a builtin type) is really straightforward:
[cc lang=”cpp”]
typedef std::chrono::duration
[/cc]
This defines a “weeks” type. The ratio of one second to one week is 604800:1. To express this, we use the std::ratio type. If we had wanted to create a type to represent tenth’s of a second, we could just do
[cc lang=”cpp”]
typedef std::chrono::duration
[/cc]
One does have to know that the “unit” time is seconds, but everything else falls out nicely and the representation is pretty clear (at least to me).
Akin to the duration type, there’s also a time_point type, and arithmetic operations on these work just like one might expect. Take for example, the following:
[cc lang=”cpp”]
auto dt = endTime-startTime;
[/cc]
We subtract the time_point startTime from the time_point endTime, and we get a duration that we can cast into any units we like. What’s the number of seconds represented by dt?
[cc lang=”cpp”]
auto numSecs = duration_cast
[/cc]
It’s as simple as that. We don’t need to know if startTime and endTime are millisecond counts, or seconds since epoch.
All in all, I’m fairly happy with the timing facilities in C++11, and I encourage you to check them out. You can take a look at the source code of the progress bar if you want to see (a little bit more), or just look through the docs and play around with std::chrono yourself. Either way, I got a nice little progress bar library out of my efforts, and I even added a few new features beyond the retrofitting of the timing facilities (compile the progress bar with HAVE_ANSI_TERM for a little bit of fun). I’ll probably continue to add more features and configuration options as I find them useful, so feel free to use the progress bar in your C++ project or to fork me on github!
Progress bars are the way to go – they shave off up to 15% of runtime:)
RobProg can shave off up to 20% thanks to the colors (if you compile with -DHAVE_ANSI_TERM ;-P).
We want to see pictures of said progress bars. Otherwise – never happened.
(T;GTF)