For a long time, standard way to precisely measure time in Rust was time
crate and its time::precise_time_ns
function. However, time
crate is deprecated now, and std
library has std::time::Instant
intended for measuring elapsed time.
I'm not sure it has the same precision, at least by design. I know it might be a vague question because there are different implementation of both things for different OSes, and implementations may change in different versions, but at least do they have the same purpose? Is std::time::Duration
a correct replacement for time::precise_time_ns
at least from the viewpoint of their design?
Running this script on my system (Mac OS) outputs quite small durations, so it's probably quite precise:
use std::time::Instant;
fn main() {
let mut t = Instant::now();
loop {
println!("{:?}", t.elapsed());
t = Instant::now();
}
}
40ns
42ns
41ns
45ns
40ns
41ns
40ns
40ns
41ns
41ns
41ns
40ns
40ns
40ns
Yes, with high certainty, std::time::Instant
is a correct replacement for time::precise_time_ns
, having the same, or better, precision.
As of Rust 1.33.0, time
0.1.41, implementations for most OSes of time::precise_time_ns()
and std::time::Instant::now()
are the same, with few exceptions.
clock_gettime(CLOCK_MONOTONIC, ...)
mach_absolute_time
QueryPerformanceCounter
time
has no implementation, std
uses TimeSysCall::perform(TimeClock::Monotonic)
std
has more correct implementationIt's unlikely that in future versions std::time::Instant::now
implementations will worsen.
time
crate has all implementations in single file, with cfg flags, standard library has directory per system, with mod.rs where implementation is chosen at compile time (unix also has conditional compilation for mac os inside time.rs
).
Both implementations use clock_gettime (3)
with CLOCK_MONOTONIC
clock_id
.
#[cfg(all(not(target_os = "macos"), not(target_os = "ios")))]
let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 };
unsafe {
libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts);
}
(ts.tv_sec as u64) * 1000000000 + (ts.tv_nsec as u64)
#[cfg(unix)]
+ #[cfg(not(any(target_os = "macos", target_os = "ios")))]
Instant { t: now(libc::CLOCK_MONOTONIC) }
Both implementations use mach_absolute_time
.
BTW, standard clock_gettime(CLOCK_MONOTONIC, ...)
works on my system too, Mac OS 10.13.6, but I'm not sure if it's really monotonic.
#[cfg(any(target_os = "macos", target_os = "ios"))]
unsafe {
let time = libc::mach_absolute_time();
let info = info();
time * info.numer as u64 / info.denom as u64
}
#[cfg(unix)]
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
Instant { t: unsafe { libc::mach_absolute_time() } }
Both implementations use QueryPerformanceCounter
#[cfg(windows)]
let mut ticks = i64_to_large_integer(0);
unsafe {
assert!(QueryPerformanceCounter(&mut ticks) == 1);
}
mul_div_i64(large_integer_to_i64(ticks), 1000000000, frequency()) as u64
#[cfg(windows)]
let mut t = Instant { t: 0 };
cvt(unsafe {
c::QueryPerformanceCounter(&mut t.t)
}).unwrap();
t
It's probably for non-web use, and unrelated to web-sys. It time
crate it's unimplemented.
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
unimplemented!()
#[cfg(target_arch = "wasm32")]
Instant(TimeSysCall::perform(TimeClock::Monotonic))
Both implementations use clock_gettime(CLOCK_MONOTONIC, ...)
, the same as for unux.
#[cfg(target_os = "redox")]
let mut ts = syscall::TimeSpec { tv_sec: 0, tv_nsec: 0 };
syscall::clock_gettime(syscall::CLOCK_MONOTONIC, &mut ts).unwrap();
(ts.tv_sec as u64) * 1000000000 + (ts.tv_nsec as u64)
#[cfg(target_os = "redox")]
Instant { t: now(syscall::CLOCK_MONOTONIC) }
Here implementations differ. time
crate falls back to std, and uses non-monotonic time (there was no monotonic time in std at the time, probably). Probably migrating from time to std improves accuracy because it uses SGX-specific call.
#[cfg(target_env = "sgx")]
// This unwrap is safe because current time is well ahead of UNIX_EPOCH, unless system clock is adjusted backward.
let std_duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
std_duration.as_secs() * NANOS_PER_SEC + std_duration.subsec_nanos() as u64
#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
Instant(usercalls::insecure_time())
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With