Долгое время стандартным способом точного измерения времени в 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
Реализация зависит от платформы, поэтому вам, вероятно, придется самостоятельно сравнивать код для соответствующих платформ. Общее качество кода стандартной библиотеки Rust высокое, поэтому я предположил бы, что реализация в std::time
настолько точна, насколько позволяет целевая платформа.
Похоже, документацию можно улучшить. Он должен либо указывать гарантированную точность, либо явно объяснять, что никаких гарантий не дается. Есть это очень актуальная проблема.
Да, с высокой степенью уверенности, 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 имеет все реализации в одном файле, с флагами cfg, стандартная библиотека имеет каталог для каждой системы, с mod.rs, где реализация выбирается во время компиляции (в unix также есть условная компиляция для mac os внутри time.rs
).
Обе реализации используют 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
Вероятно, это не для использования в Интернете и не связано с веб-система. Это 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) }
Здесь реализации различаются. 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())
По крайней мере, в Linux Oниобе используют
clock_gettime (CLOCK_MONOTONIC)
, поэтому они должны давать одинаковые результаты.