What is the resolution of std.time.Timer and is it good enough?

Let me preface this with, I haven’t researched this in enough depth, but maybe we can do this research together.

So the two questions are:

  • what are the resolutions for std.time.Timer depending on platforms?
  • are these resolutions good enough?
    (for example to be used as accurate measurements for benchmarks)

From this video:

it seems that:

  • window’s query performance counter is bad?
    it botches a high precision measurement into a low precision value
  • zig uses queryPerformanceCounter on windows src/std/time.zig
  • using rdtscp directly gives access to greater precision,
    can we write a zig library that makes use of that?
  • would that allow us to get more accurate timings, or do I misunderstand something?
  • If so are there similar instructions for different architectures?
  • Ideally someone from the community does the process from the video
    to see whether the zig code results in throwing away a lot of precision on windows
  • what is the precision on other operating systems?
  • Can we develop some benchmarks for timing itself and how accurate it is?
1 Like

After seeing this topic, I couldn’t help settings this up :wink:

The code just queries the timer but basically does nothing else. Sample output:

zig build bench-noop --release=fast && ./zig-out/bin/bench-noop
noop!, n=10, avg: 16, std: 8
noop!, n=100, avg: 12, std: 1
noop!, n=1000, avg: 14, std: 4
noop!, n=10000, avg: 15, std: 10
noop!, n=100000, avg: 14, std: 6
noop!, n=1000000, avg: 13, std: 25

I originally stumbled across this while looking at an issue on zbench. I’m not 100% sure how meaningful that is, but the results vary quite drastically if you run this multiple times (at least on my Linux machine). Also, standard deviations tend to outnumber the average at higher N, which is …not a good sign.

1 Like

Two functions that returns the CPU ticks for aarch64 and x86_64 architectures:

fn aarch64_ticks() u64 {
    return asm volatile(
        "mrs %[ret], CNTVCT_EL0"
        : [ret] "=r" (-> u64)
    );
}

fn x86_64_ticks() u64 {
    return asm volatile(
        \\ rdtsc
        \\ shl $32, %%rdx
        \\ or %%rdx, %%rax
        : [ret] "={rax}" (-> u64)
        :
        : "rdx"
    );
}
4 Likes

besides, I think @nyc has some more code around this topic on GitHub - jnordwick/tempus: fast, minimal clocks, TSC, dates, and timestamps for zig

to linux posix:

valide version 15 … last 16.dev

// time posix retrieve timstamp nanoseconds
fn TSnano() i128 {
    const ts = std.posix.clock_gettime(.REALTIME) catch |err| switch (err) {
        error.UnsupportedClock, error.Unexpected => return 0, // "Precision of timing depends on hardware and OS".
    };
    return (@as(i128, ts.sec) * std.time.ns_per_s) + ts.nsec;
}  

16.dev remove std.time.nanoTimestamp()

That functionality is now part of std.Io

const timestamp = try std.Io.Clock.real.now(io);

or

const clock: std.Io.Clock = .real;
const timestamp = try clock.now(io);

It returns an Io.Timestamp. See also Io.Clock

(side note: there’s an unresolved ambiguity related to the .real clock on Windows currently)

2 Likes

Where does “now(io)” come from? Please, io???

1 Like

ok I understand
ex: This is for a chronological follow-up.

fn TSnano() i128 {
        var threaded: std.Io.Threaded = .init_single_threaded;
        const io = threaded.io();
        const ts = std.Io.Clock.real.now(io) catch |err| switch (err) {
        error.UnsupportedClock, error.Unexpected => return 0, 
    };
    return @as(i128, ts.nanoseconds);
}  

merci , Thanks for the help.

In general I would be very careful about depending on timer resolution, for instance on Emscripten you have a POSIX-like time API, but the returned timer may be clamped to 1 or 2 milliseconds(!) on some browsers and with random jitter on top (e.g. sometimes you get a 16.667 frametime back as 16ms and sometimes as 17ms - at least this is random enough that averaging enough values arrives back at 16.667ms).

But such randomized time sources are entirely useless for profiling of course.

1 Like