std :: time :: Duration是否与“时间”板条箱中的time :: precise_time_ns一样精确?

时间:2019-04-09 00:25:23

标签: time rust

很长一段时间以来,time板条箱及其time::precise_time_ns功能是在Rust中精确测量时间的标准方法。但是,time条板箱现在已过时,std库中有std::time::Instant个用于测量经过时间的文件。

至少在设计上,我不确定它是否具有相同的精度。我知道这可能是一个模糊的问题,因为对于不同的操作系统,两者的实现方式不同,并且实现方式可能在不同的版本中有所变化,但是至少它们具有相同的目的吗?至少从其设计的角度来看,std::time::Durationtime::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

1 个答案:

答案 0 :(得分:2)

是的,std::time::Instant可以肯定地代替time::precise_time_ns,具有相同或更好的精度。

从Rust 1.33.0 time 0.1.41开始,time::precise_time_ns()std::time::Instant::now()的大多数操作系统的实现都是相同的,只有少数例外。

  • Unix 相同clock_gettime(CLOCK_MONOTONIC, ...)
  • MacOS 相同mach_absolute_time
  • Windows 相同QueryPerformanceCounter
  • Wasm32 time没有实现,std使用TimeSysCall::perform(TimeClock::Monotonic)
  • 氧化还原相同,与UNIX相同
  • SGX 实现方式不同,可能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内部也有针对macOS的条件编译)。

Unix,“常规”,不是 MacOS或iOS

两个实现都将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)

std

#[cfg(unix)] + #[cfg(not(any(target_os = "macos", target_os = "ios")))]

Instant { t: now(libc::CLOCK_MONOTONIC) }

Unix,MacOS或iOS

两个实现都使用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
}

std

#[cfg(unix)] + #[cfg(any(target_os = "macos", target_os = "ios"))]

Instant { t: unsafe { libc::mach_absolute_time() } }

Windows

两个实现都使用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

std

#[cfg(windows)]

let mut t = Instant { t: 0 };
cvt(unsafe {
    c::QueryPerformanceCounter(&mut t.t)
}).unwrap();
t

Wasm32

它可能用于非Web使用,并且与web-sys无关。 time认为它没有实现。

时间

#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]

unimplemented!()

std

#[cfg(target_arch = "wasm32")]

Instant(TimeSysCall::perform(TimeClock::Monotonic))

氧化还原

两个实现都使用clock_gettime(CLOCK_MONOTONIC, ...),与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)

std

#[cfg(target_os = "redox")]

Instant { t: now(syscall::CLOCK_MONOTONIC) }

SGX

此处的实现有所不同。 time的条板箱会退回到标准状态,并使用非单调时间(可能当时标准中没有单调时间)。由于它使用SGX特定的调用,因此可能不时迁移到std可以提高准确性。

时间

#[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

std

#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]

Instant(usercalls::insecure_time())