I was curious so I also ran your program on my machine. I observe the behavior you describe in Debug mode, but not in ReleaseFast or even ReleaseSafe. I imagine there’s something special happening with the Io interface in debug mode.
Try a warmup loop before you measure. Your first call is likely sneaking in before the OS triggers an unrelated set of operations on startup. cpu frequency change for example.
Ok, so I’m home, and on my desktop Linux (x86-64 - Android is aarch64) - also in Debug mode - I get less consistent results, which is actually less surprising to me than having the weird delay on the second call every time: (changed clk to .real which makes no difference)
I suspect you’re on to something. Inserting (before clk.now):
for (0..500_000) |_| {
std.atomic.spinLoopHint();
}
makes the subsequent untilNow calls more consistent (around 300ns) on desktop Linux, but now on Android the delay of the second untilNow call is consistently at around 13us.
Note that just because it appears after the second call, does not mean it originated from the second call.
Internally untilNow does some stuff, then it gets the real time stamp, then it does some more stuff. That second part is not included in the first measurement.
As far as I can see untilNow (in debug mode) calls into multiple functions (→most likely icache misses) after it measured the time to compute the time difference. So this is a possibly explanation.
EDIT: never mind, it’s probably because my computer has heterogenous CPU cores. It happens every I delete the zig-out directory - I think the compilation process makes the scheduler think it’s CPU-bound and put it onto a performance core.
On modern operating systems (e.g. not 1980s 8- and 16-bit home computers) I would fully expect such random noise up to even milliseconds (assuming this is ‘wall clock time’).
Your program can be randomly interrupted at any time by the operating system’s scheduler and you have absolutely no control over when and for how long such scheduling gaps might occur. On top of that there may be random caching/warmup effects, or variable CPU clocks or performance-vs-efficiency cores on laptops.
Modern operating systems (and the hardware they run on) are simply the complete opposite of ‘realtime’, and they may suffer a lot from ‘noisy neighbours’.
For instance when running a game loop it’s not unusual that the per-frame code has a ‘scheduling jitter’ of one or two milli(!)seconds when the code is vsync-throttled, e.g. even though the presentation itself is absolutely on time, that doesn’t mean that your render thread wakes up in a predictable time window after vsync occured, it can be anywhere between 0.5 and 2 milliseconds late.
TL;DR: any sort of accurate time measurement on modern operating systems is a complete mess and shouldn’t be trusted
Preaching to the choir. I much prefer microcontrollers in that respect, much less stuff in between your code and the cpu. Just the other day I needed to make an ESP32-S3 output a PPS signal, and even with the most trivial code it was < 10us of jitter.
Thanks for the responses. I’m beginning some debugging of hairy multithreaded code and I did this small experiment to figure out roughly what to expect from time measurements in terms of reliability / consistency. Now I won’t be too surprised to see random delays on the order of 10us.