很长一段时间,精确测量Rust时间的标准方法是time
crate及其time::precise_time_ns
功能。但是,time
crate现已弃用,std
库有std::time::Instant
用于测量经过的时间。
我不确定它是否具有相同的精度,至少在设计上如此。我知道这可能是一个模糊的问题,因为不同的操作系统有不同的实现,实现可能会在不同的版本中发生变化,但至少他们有相同的目的吗? std::time::Duration
至少从他们的设计角度来看是time::precise_time_ns
的正确替代品吗?
在我的系统(Mac OS)上运行此脚本会输出相当短的持续时间,因此它可能非常精确:
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
是的,确切地说,std::time::Instant
是time::precise_time_ns
的正确替代品,具有相同或更好的精度。
从Rust 1.33.0开始,time
0.1.41,time::precise_time_ns()
和std::time::Instant::now()
的大多数操作系统的实现是相同的,除了少数例外。
clock_gettime(CLOCK_MONOTONIC, ...)
mach_absolute_time
QueryPerformanceCounter
time
没有实现,std
使用TimeSysCall::perform(TimeClock::Monotonic)
std
有更正确的实现在未来的版本中,std::time::Instant::now
的实现不太可能会恶化。
time
crate has all implementations in single file,带有cfg标志,标准库有每个系统的目录,有mod.rs where implementation is chosen at compile time(unix也有time.rs
内的mac os的条件编译)。
两种实现都使用clock_gettime (3)
和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) }
两种实现都使用mach_absolute_time
。
顺便说一句,标准clock_gettime(CLOCK_MONOTONIC, ...)
也适用于我的系统,Mac OS 10.13.6,但我不确定它是否真的是单调的。
#[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() } }
两种实现都使用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
它可能用于非网络使用,与web-sys无关。它time
箱子没有实现。
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
unimplemented!()
#[cfg(target_arch = "wasm32")]
Instant(TimeSysCall::perform(TimeClock::Monotonic))
两种实现都使用qazxsw poi,与unix相同。
clock_gettime(CLOCK_MONOTONIC, ...)
#[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) }
crate回到std,并使用非单调时间(当时std没有单调时间,可能)。可能从时间到std的迁移提高了准确性,因为它使用特定于SGX的呼叫。
time
#[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"))]