Является ли std :: time :: Duration такой же точной, как time :: precision_time_ns из ящика "time"? - PullRequest
4 голосов
/ 09 апреля 2019

Долгое время стандартным способом точного измерения времени в Rust было time ящик и его функция time::precise_time_ns. Однако, time корзина устарела, а библиотека 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

1 Ответ

2 голосов
/ 10 апреля 2019

Да, с высокой степенью достоверности, 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)
  • Redox : такой же , такой же как unix
  • SGX : реализации отличаются , вероятно std имеет более правильную реализацию

Маловероятно, что в будущих версиях реализация std::time::Instant::now ухудшится.

Подробности реализации

time crate имеет все реализации в одном файле , с флагами cfg, в стандартной библиотеке есть каталог на систему, с mod.rs, где реализация выбирается во время компиляции (unix также есть условная компиляция для Mac OS внутри time.rs).

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)

станд

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

станд

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

станд

#[cfg(windows)]

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

Wasm32

Это, вероятно, не для веб-использования и не связано с web-sys . Это time Ящик не реализован.

время

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

unimplemented!()

станд

#[cfg(target_arch = "wasm32")]

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

Redox

Обе реализации используют 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)

станд

#[cfg(target_os = "redox")]

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

SGX

Здесь реализации отличаются. time Ящик возвращается к стандартному стандарту и использует немонотонное время (в то время стандартное время не было, вероятно, в стандартном режиме). Вероятно, переход от времени к стандартному стандарту повышает точность, поскольку он использует специфичный для SGX вызов.

время

#[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())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...