0

I'm doing work involving benchmarking algorithms. I read about the new <chrono> header in C++11, so I went with that.

I can take measurements and everything, but I am struggling with resolution.

When doing something like

auto duration = chrono::duration_cast<chrono::nanoseconds>(end_time - start_time).count();

I consistently get times that are multiples of 1000!

Upon investigating further and doing the following

cout << (double) chrono::high_resolution_clock::period::num / 
chrono::high_resolution_clock::period::den << endl;

I got a value of 1e-06 which is microseconds, not nanoseconds. It casts to nanoseconds fine, but it's useless if if the period of the clock is only in microseconds to start with.

Am I being pedantic? I know I can run my test code multiple times and get a nice large average time to work with, and that is what I'm doing. But it's almost a matter of principle for me.

Extra Info: I use the latest version of GCC (4.6.3) on Ubuntu 12.04 server X64 (I think)

getack
  • 172
  • 7
  • 6
    Yes, you've shown that the resolution of `high_resolution_clock` in this particular implementation is 1µs. What is your question? – eerorika May 24 '14 at 10:37
  • the resolution of the timers (even the high resolution ones) are platform specific. I had experiences in Solaris deployments where timers had resolutions of 10 ms.... so you're not so bad. – jsantander May 24 '14 at 10:52
  • 4
    There's not a lot of point to chasing nanosecond accuracy when you run a benchmark on a machine with an operating system that supports multi-tasking and a processor that supports variable clock rates and heavily depends on branch prediction and cache state to execute code fast. Any lack of timing resolution can be addressed by running the test multiple times. – Hans Passant May 24 '14 at 11:52
  • It's not affecting me in any way, I was just curious. I was initially working on Windows and was really pissed when I realised the resolution was milliseconds or something (It's an acknowledged bug). After I ported _all_ my code over to GCC I thought lemme try the test again, and then I came here out of frustration. I was just under impression that `duration_cast` would actually give me nanoseconds and not just whatever that specific implementation's maintainer decided to do as in MSVC++'s case. – getack May 24 '14 at 17:51
  • @Hans Passant I want to emphasize the point that Hans has made. You can get wildly varying results due to preemption, CPU governors, cache misses (again due to preemption) etc. Never run a test just once, repeat the heck out of it. My computer runs benchmarks on my code most nights now. I exit my xwindow session and shut down everything I can and let it run many times to get as clean of a result as possible. – Daniel Santos Jul 30 '14 at 23:07

3 Answers3

3

Fwiw, for me, this:

#include <ratio>
#include <chrono>
#include <string>

template <class Ratio, class CharT>
struct ratio_string
{
    static std::basic_string<CharT> symbol()
    {
        return std::basic_string<CharT>(1, CharT('[')) +
               std::to_string(Ratio::num) + CharT('/') +
               std::to_string(Ratio::den) + CharT(']');
    }
};

template <class CharT>
struct ratio_string<std::nano, CharT>
{
    static std::basic_string<CharT> symbol()
    {
        return std::basic_string<CharT>(1, CharT('n'));
    }
};

template <>
struct ratio_string<std::micro, char>
{
    static std::string symbol() {return std::string("\xC2\xB5");}
};

template <>
struct ratio_string<std::micro, char16_t>
{
    static std::u16string symbol() {return std::u16string(1, u'\xB5');}
};

template <>
struct ratio_string<std::micro, char32_t>
{
    static std::u32string symbol() {return std::u32string(1, U'\xB5');}
};

template <>
struct ratio_string<std::micro, wchar_t>
{
    static std::wstring symbol() {return std::wstring(1, L'\xB5');}
};

template <class CharT, class Rep, class Period>
inline
std::basic_string<CharT>
get_unit(const std::chrono::duration<Rep, Period>& d)
{
    return ratio_string<Period, CharT>::symbol() + 's';
}

template <class Rep, class Period>
std::string
to_string(const std::chrono::duration<Rep, Period>& d)
{
    return std::to_string(d.count()) + get_unit<char>(d);
}

#include <iostream>

int
main()
{
    auto t0 = std::chrono::high_resolution_clock::now();
    auto t1 = std::chrono::high_resolution_clock::now();
    std::cout << to_string(t1-t0) << '\n';
}

when compiled with -O3, outputs:

$ a.out
112ns
$ a.out
95ns
$ a.out
112ns
$ a.out
111ns

YMMV. Your output may be neater if you add ratio_string specializations.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
2

The precision of clock is hardware and operating system dependent, on x86 platform running linux microseconds is quite normal. On my red hat 6 with 2.6.30 kernel, I can only get about 10µs.

To get better resolution, you'll need a real time operating system.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
swang
  • 5,157
  • 5
  • 33
  • 55
  • Thought as much - thanks! I was just curious. I forgot about the RT kernel thing! One day I will experiment with a RT kernel and see what differences there are. – getack May 24 '14 at 17:46
  • Back in kernel 2.6, Linux kernel timekeeping was largely done around jiffies, 1/100th second. The kernel `ktime` nanosecond type was only added in 2.6.15. But 2.6 is very old now. Even in 2014, kernel 2.6 was pretty old! Linux will give you nanosecond timestamps as a matter of course. I'm not aware of the RT kernel ever changing timer resolution. Primarily it was about interrupt latency. – TrentP Sep 08 '21 at 17:25
0

I was just under impression that duration_cast would actually give me nanoseconds and not just whatever that specific implementation's maintainer decided to do as in MSVC++'s case.

No, duration_cast will simply convert the duration as-is to another type. It will not change how the clock operates internally or change the clock's resolution.

In general it's a good idea not to do these casts or conversions, and instead just allow the implementation to pick the appropriate type. It's also simpler this way:

auto duration = end_time - start_time;

And with Howard Hinnant's chrono_io you can just print this out directly and it will give you the right units.

std::cout << duration << '\n';

That would print out something like 112ns, 2us, etc. depending on the actual resolution.

bames53
  • 86,085
  • 15
  • 179
  • 244
  • I use the new auto type everywhere. The only place that I use the cast is for display purposes. – getack May 29 '14 at 08:24