Является ли std::time::Duration таким же точным, как time::precise_time_ns из ящика «время»?

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

По крайней мере, в Linux Oниобе используют clock_gettime (CLOCK_MONOTONIC), поэтому они должны давать одинаковые результаты.

Jmb 09.04.2019 08:37

Реализация зависит от платформы, поэтому вам, вероятно, придется самостоятельно сравнивать код для соответствующих платформ. Общее качество кода стандартной библиотеки Rust высокое, поэтому я предположил бы, что реализация в std::time настолько точна, насколько позволяет целевая платформа.

Sven Marnach 09.04.2019 09:23

Похоже, документацию можно улучшить. Он должен либо указывать гарантированную точность, либо явно объяснять, что никаких гарантий не дается. Есть это очень актуальная проблема.

Lukas Kalbertodt 09.04.2019 09:40
Работа с датами и временем в языке Java
Работа с датами и временем в языке Java
Работа с датами и временем в языке Java была сильно переработана начиная с версии Java 8 и далее с появлением библиотеки java.time.
7
3
956
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Да, с высокой степенью уверенности, 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, ...)
  • MacOS: такой же, mach_absolute_time
  • Окна: такой же, QueryPerformanceCounter
  • Васм32: time не имеет реализации, std использует TimeSysCall::perform(TimeClock::Monotonic)
  • окислительно-восстановительный: такой же, то же, что и unix
  • SGX: реализации различаются, наверное std имеет более правильную реализацию

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

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

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

Unix, "обычный", нет MacOS или iOS

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

Окна

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

Васм32

Вероятно, это не для использования в Интернете и не связано с веб-система. Это time ящик, который не реализован.

время

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

unimplemented!()

стандарт

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

стандарт

#[cfg(target_os = "redox")]

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

SGX

Здесь реализации различаются. time crate возвращается к стандартному и использует немонотонное время (в то время, вероятно, не было монотонного времени в стандартном стандарте). Вероятно, переход с time на std повышает точность, поскольку использует специфичный для 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())

Другие вопросы по теме