diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b3c8dd4..8d2b6e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [ master ] + branches: [ main ] pull_request: - branches: [ master ] + branches: [ main ] env: CARGO_TERM_COLOR: always @@ -13,37 +13,38 @@ jobs: build: runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: - os: [macos-latest, ubuntu-latest, windows-latest] - features: ["atomic", "atomic,fallback-coarse"] - rust: [stable] + os: [ macos-latest, ubuntu-latest, windows-latest ] + features: [ "atomic", "atomic,fallback-coarse" ] + rust: [ stable ] env: RUST_BACKTRACE: 1 steps: - - uses: actions/checkout@v2 - - name: Set up toolchains - uses: actions-rs/toolchain@v1 - with: + - uses: actions/checkout@v4 + - uses: Swatinem/rust-cache@v2 + - name: Set up toolchains + uses: dtolnay/rust-toolchain@master + with: toolchain: ${{ matrix.rust }} - override: true components: rustfmt, clippy - - name: Check format - run: cargo fmt --all -- --check - - name: Build - run: cargo build --workspace --all-targets --features ${{ matrix.features }} - - name: Clippy - run: cargo clippy --workspace --all-targets --features ${{ matrix.features }} -- -D warnings - - name: Run tests - run: cargo test --workspace --all-targets --features ${{ matrix.features }} -- --nocapture - - name: Run benches - run: cargo bench --workspace --all-targets --features ${{ matrix.features }} + - name: Check format + run: cargo fmt --all -- --check + - name: Build + run: cargo build --workspace --all-targets --features ${{ matrix.features }} + - name: Clippy + run: cargo clippy --workspace --all-targets --features ${{ matrix.features }} -- -D warnings + - name: Run tests + run: cargo test --workspace --all-targets --features ${{ matrix.features }} -- --nocapture + - name: Run benches + run: cargo bench --workspace --all-targets --features ${{ matrix.features }} build-wasm: runs-on: ubuntu-latest env: RUST_BACKTRACE: 1 steps: - - uses: actions/checkout@v2 - - name: Set up toolchains - run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - - run: wasm-pack test --node + - uses: actions/checkout@v4 + - name: Set up toolchains + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + - run: wasm-pack test --node diff --git a/Cargo.toml b/Cargo.toml index 15f900e..1ada95c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,21 +1,22 @@ [package] -name = "minstant" -version = "0.1.7" -authors = ["The TiKV Authors"] +name = "fastant" +version = "0.1.0" +authors = ["FastLabs Developers"] edition = "2021" license = "MIT" -description = "A drop-in replacement for `std::time::Instant` that measures time with high performance and high accuracy powered by TSC" -homepage = "https://github.com/tikv/minstant" -repository = "https://github.com/tikv/minstant" -documentation = "https://docs.rs/minstant" +description = "A drop-in replacement for `std::time::Instant` that measures time with high performance and high accuracy powered by Time Stamp Counter (TSC)." +homepage = "https://github.com/fast/fastant" +repository = "https://github.com/fast/fastant" +documentation = "https://docs.rs/fastant" readme = "README.md" keywords = ["TSC", "clock", "rdtsc", "timing", "nanosecond"] + [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] -ctor = "0.1.20" +ctor = "0.2" coarsetime = { version = "0.1", optional = true } web-time = "1.0" @@ -24,8 +25,11 @@ atomic = [] fallback-coarse = ["coarsetime"] [dev-dependencies] -criterion = "0.3" -quanta = "0.9" +criterion = { version = "0.5", default-features = false, features = [ + "plotters", + "cargo_bench_support", +] } +quanta = "0.12" rand = "0.8" wasm-bindgen-test = "0.3" getrandom = { version = "0.2", features = ["js"] } diff --git a/README.md b/README.md index 32cb727..53bf950 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,37 @@ -# minstant -[![Actions Status](https://github.com/tikv/minstant/workflows/CI/badge.svg)](https://github.com/tikv/minstant/actions) -[![Build Status](https://travis-ci.org/tikv/minstant.svg?branch=master)](https://travis-ci.org/tikv/minstant) -[![Documentation](https://docs.rs/minstant/badge.svg)](https://docs.rs/minstant/) -[![Crates.io](https://img.shields.io/crates/v/minstant.svg)](https://crates.io/crates/minstant) -[![LICENSE](https://img.shields.io/github/license/tikv/minstant.svg)](https://github.com/tikv/minstant/blob/master/LICENSE) +# Fastant -A drop-in replacement for [`std::time::Instant`](https://doc.rust-lang.org/std/time/struct.Instant.html) that measures time with high performance and high accuracy powered by [TSC](https://en.wikipedia.org/wiki/Time_Stamp_Counter). +A drop-in replacement for [`std::time::Instant`](https://doc.rust-lang.org/std/time/struct.Instant.html) that measures time with high performance and high accuracy powered by [Time Stamp Counter (TSC)](https://en.wikipedia.org/wiki/Time_Stamp_Counter). + +[![Actions Status](https://github.com/fast/fastant/workflows/CI/badge.svg)](https://github.com/fast/fastant/actions) +[![Documentation](https://docs.rs/fastant/badge.svg)](https://docs.rs/fastant/) +[![Crates.io](https://img.shields.io/crates/v/fastant.svg)](https://crates.io/crates/fastant) +[![LICENSE](https://img.shields.io/github/license/fast/fastant.svg)](LICENSE) ## Usage ```toml [dependencies] -minstant = "0.1" +fastant = "0.1" ``` ```rust -let start = minstant::Instant::now(); - -// Code snipppet to measure - -let duration: std::time::Duration = start.elapsed(); +fn main() { + let start = fastant::Instant::now(); + let duration: std::time::Duration = start.elapsed(); +} ``` - ## Motivation -This library is used by a high performance tracing library [`minitrace-rust`](https://github.com/tikv/minitrace-rust). The main purpose is to use [TSC](https://en.wikipedia.org/wiki/Time_Stamp_Counter) on x86 processors to measure time at high speed without losing much accuracy. +This library is used by a high performance tracing library [`fastrace`](https://github.com/fast/fastrace). The main purpose is to use [Time Stamp Counter (TSC)](https://en.wikipedia.org/wiki/Time_Stamp_Counter) on x86 processors to measure time at high speed without losing much accuracy. ## Platform Support -Currently, only the Linux on `x86` or `x86_64` is backed by [TSC](https://en.wikipedia.org/wiki/Time_Stamp_Counter). On other platforms, `minstant` falls back to `std::time`. If TSC is unstable, it will also fall back to `std::time`. +Currently, only the Linux on `x86` or `x86_64` is backed by Time Stamp Counter (TSC). On other platforms, Fastant falls back to `std::time`. If TSC is unstable, it will also fall back to `std::time`. If speed is privileged over accuracy when fallback occurs, you can use `fallback-coarse` feature to use coarse time: ```toml [dependencies] -minstant = { version = "0.1", features = ["fallback-coarse"] } -``` - -## Benchmark - -Benchmark platform is `Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz` on CentOS 7. - -```sh -> cargo criterion - -Instant::now()/minstant time: [10.449 ns 10.514 ns 10.619 ns] -Instant::now()/quanta time: [31.467 ns 31.628 ns 31.822 ns] -Instant::now()/std time: [26.831 ns 26.924 ns 27.016 ns] -minstant::Anchor::new() time: [46.987 ns 47.243 ns 47.498 ns] -minstant::Instant::as_unix_nanos() time: [15.287 ns 15.318 ns 15.350 ns] +fastant = { version = "0.1", features = ["fallback-coarse"] } ``` - -![Benchmark](benchmark.jpeg) diff --git a/benches/criterion.rs b/benches/criterion.rs index 4ef925c..f8ed27a 100644 --- a/benches/criterion.rs +++ b/benches/criterion.rs @@ -1,12 +1,15 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use criterion::black_box; +use criterion::criterion_group; +use criterion::criterion_main; +use criterion::Criterion; -fn bench_nows(c: &mut Criterion) { - // The first call will take some time for calibartion +fn bench_now(c: &mut Criterion) { + // The first call will take some time for calibration quanta::Instant::now(); let mut group = c.benchmark_group("Instant::now()"); - group.bench_function("minstant", |b| { - b.iter(minstant::Instant::now); + group.bench_function("fastant", |b| { + b.iter(fastant::Instant::now); }); group.bench_function("quanta", |b| { b.iter(quanta::Instant::now); @@ -18,19 +21,19 @@ fn bench_nows(c: &mut Criterion) { } fn bench_anchor_new(c: &mut Criterion) { - c.bench_function("minstant::Anchor::new()", |b| { - b.iter(minstant::Anchor::new); + c.bench_function("fastant::Anchor::new()", |b| { + b.iter(fastant::Anchor::new); }); } fn bench_as_unix_nanos(c: &mut Criterion) { - let anchor = minstant::Anchor::new(); - c.bench_function("minstant::Instant::as_unix_nanos()", |b| { + let anchor = fastant::Anchor::new(); + c.bench_function("fastant::Instant::as_unix_nanos()", |b| { b.iter(|| { - black_box(minstant::Instant::now().as_unix_nanos(&anchor)); + black_box(fastant::Instant::now().as_unix_nanos(&anchor)); }); }); } -criterion_group!(benches, bench_nows, bench_anchor_new, bench_as_unix_nanos); +criterion_group!(benches, bench_now, bench_anchor_new, bench_as_unix_nanos); criterion_main!(benches); diff --git a/benchmark.jpeg b/benchmark.jpeg deleted file mode 100644 index ac6297e..0000000 Binary files a/benchmark.jpeg and /dev/null differ diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..f73a725 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,12 @@ +comment_width = 120 +edition = "2021" +format_code_in_doc_comments = true +group_imports = "StdExternalCrate" +imports_granularity = "Item" +normalize_comments = true +overflow_delimited_expr = true +reorder_imports = true +trailing_comma = "Vertical" +version = "Two" +where_single_line = true +wrap_comments = true diff --git a/src/instant.rs b/src/instant.rs index 03e3099..da0daa0 100644 --- a/src/instant.rs +++ b/src/instant.rs @@ -1,12 +1,15 @@ // Copyright 2021 TiKV Project Authors. Licensed under Apache-2.0. -use std::{ - ops::{Add, AddAssign, Sub, SubAssign}, - time::Duration, -}; -use web_time::{SystemTime, UNIX_EPOCH}; +use std::ops::Add; +use std::ops::AddAssign; +use std::ops::Sub; +use std::ops::SubAssign; +use std::time::Duration; -/// A measurement of a monotonically nondecreasing clock. Similar to +use web_time::SystemTime; +use web_time::UNIX_EPOCH; + +/// A measurement of a monotonically non-decreasing clock. Similar to /// [`std::time::Instant`](std::time::Instant) but is faster and more /// accurate if TSC is available. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -22,9 +25,8 @@ impl Instant { /// /// # Examples /// - /// ``` - /// use minstant::Instant; - /// + /// ```rust + /// use fastant::Instant; /// let now = Instant::now(); /// ``` pub fn now() -> Instant { @@ -36,20 +38,21 @@ impl Instant { /// /// # Panics /// - /// Previously we panicked if `earlier` was later than `self`. Currently this method saturates + /// Previously we panicked if `earlier` was later than `self`. Currently, this method saturates /// to follow the behavior of the standard library. Future versions may reintroduce the panic /// in some circumstances. /// /// # Examples /// /// ``` - /// use std::time::Duration; /// use std::thread::sleep; + /// use std::time::Duration; /// - /// use minstant::Instant; + /// use fastant::Instant; /// /// let now = Instant::now(); /// sleep(Duration::new(1, 0)); + /// /// let new_now = Instant::now(); /// println!("{:?}", new_now.duration_since(now)); /// println!("{:?}", now.duration_since(new_now)); // 0ns @@ -64,13 +67,14 @@ impl Instant { /// # Examples /// /// ``` - /// use std::time::Duration; /// use std::thread::sleep; + /// use std::time::Duration; /// - /// use minstant::Instant; + /// use fastant::Instant; /// /// let now = Instant::now(); /// sleep(Duration::new(1, 0)); + /// /// let new_now = Instant::now(); /// println!("{:?}", new_now.checked_duration_since(now)); /// println!("{:?}", now.checked_duration_since(new_now)); // None @@ -87,13 +91,14 @@ impl Instant { /// # Examples /// /// ``` - /// use std::time::Duration; /// use std::thread::sleep; + /// use std::time::Duration; /// - /// use minstant::Instant; + /// use fastant::Instant; /// /// let now = Instant::now(); /// sleep(Duration::new(1, 0)); + /// /// let new_now = Instant::now(); /// println!("{:?}", new_now.saturating_duration_since(now)); /// println!("{:?}", now.saturating_duration_since(new_now)); // 0ns @@ -113,10 +118,10 @@ impl Instant { /// # Examples /// /// ``` - /// use std::time::Duration; /// use std::thread::sleep; + /// use std::time::Duration; /// - /// use minstant::Instant; + /// use fastant::Instant; /// /// let instant = Instant::now(); /// let three_secs = Duration::from_secs(3); @@ -146,18 +151,20 @@ impl Instant { .map(Instant) } - /// Convert interal clocking counter into a UNIX timestamp represented as the - /// nanoseconds elapsed from [UNIX_EPOCH](std::time::UNIX_EPOCH). + /// Convert internal clocking counter into a UNIX timestamp represented as the + /// nanoseconds elapsed from [UNIX_EPOCH](UNIX_EPOCH). /// - /// [`Anchor`](crate::Anchor) contains the necessary calibration data for conversion. - /// Typically, initializing an [`Anchor`](crate::Anchor) takes about 50 nano seconds, so + /// [`Anchor`](Anchor) contains the necessary calibration data for conversion. + /// Typically, initializing an [`Anchor`](Anchor) takes about 50 nanoseconds, so /// try to reuse it for a batch of `Instant`. /// /// # Examples /// /// ``` /// use std::time::UNIX_EPOCH; - /// use minstant::{Instant, Anchor}; + /// + /// use fastant::Anchor; + /// use fastant::Instant; /// /// let anchor = Anchor::new(); /// let instant = Instant::now(); @@ -214,7 +221,7 @@ impl Sub for Instant { /// /// # Panics /// - /// Previously we panicked if `other` was later than `self`. Currently this method saturates + /// Previously we panicked if `other` was later than `self`. Currently, this method saturates /// to follow the behavior of the standard library. Future versions may reintroduce the panic /// in some circumstances. fn sub(self, other: Instant) -> Duration { @@ -230,7 +237,7 @@ impl std::fmt::Debug for Instant { /// An anchor which can be used to convert internal clocking counter into system timestamp. /// -/// *[See also the `Instant::as_unix_nanos()`](crate::Instant::as_unix_nanos).* +/// **[See also the `Instant::as_unix_nanos()`](Instant::as_unix_nanos).** #[derive(Copy, Clone)] pub struct Anchor { unix_time_ns: u64, @@ -260,11 +267,14 @@ impl Anchor { #[cfg(all(feature = "atomic", target_has_atomic = "64"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "atomic", target_has_atomic = "64"))))] mod atomic { - use super::Instant; - use std::sync::atomic::{AtomicU64, Ordering}; + use std::sync::atomic::AtomicU64; + use std::sync::atomic::Ordering; + #[cfg(doc)] use Ordering::*; + use super::Instant; + /// Atomic variant of [`Instant`]. #[derive(Debug)] #[repr(transparent)] @@ -320,8 +330,8 @@ mod atomic { /// Loads a value from the [`Atomic`]. /// - /// `load` takes an [`Ordering`] argument which describes the memory ordering of this operation. - /// Possible values are [`SeqCst`], [`Acquire`] and [`Relaxed`]. + /// `load` takes an [`Ordering`] argument which describes the memory ordering of this + /// operation. Possible values are [`SeqCst`], [`Acquire`] and [`Relaxed`]. /// /// # Panics /// @@ -339,8 +349,8 @@ mod atomic { /// Stores a value into the [`Atomic`]. /// - /// `store` takes an [`Ordering`] argument which describes the memory ordering of this operation. - /// Possible values are [`SeqCst`], [`Release`] and [`Relaxed`]. + /// `store` takes an [`Ordering`] argument which describes the memory ordering of this + /// operation. Possible values are [`SeqCst`], [`Release`] and [`Relaxed`]. /// /// # Panics /// diff --git a/src/lib.rs b/src/lib.rs index f34d8ea..eb167da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,49 +1,46 @@ // Copyright 2021 TiKV Project Authors. Licensed under Apache-2.0. //! A drop-in replacement for [`std::time::Instant`](https://doc.rust-lang.org/std/time/struct.Instant.html) -//! that measures time with high performance and high accuracy powered by [TSC](https://en.wikipedia.org/wiki/Time_Stamp_Counter). +//! that measures time with high performance and high accuracy powered by [Time Stamp Counter (TSC)](https://en.wikipedia.org/wiki/Time_Stamp_Counter). //! //! ## Example //! -//! ``` -//! let start = minstant::Instant::now(); -//! -//! // Code snipppet to measure -//! +//! ```rust +//! let start = fastant::Instant::now(); //! let duration: std::time::Duration = start.elapsed(); //! ``` //! //! ## Platform Support //! -//! Currently, only the Linux on `x86` or `x86_64` is backed by [TSC](https://en.wikipedia.org/wiki/Time_Stamp_Counter). -//! On other platforms, `minstant` falls back to coarse time. +//! Currently, only the Linux on `x86` or `x86_64` is backed by Time Stamp Counter (TSC). +//! On other platforms, `fastant` falls back to coarse time. //! //! ## Calibration //! -//! [TSC](https://en.wikipedia.org/wiki/Time_Stamp_Counter) doesn’t necessarily ticks in constant speed and even -//! doesn't synchronize across CPU cores. The calibration detects the TSC deviation and calculates the correction -//! factors with the assistance of a source wall clock. Once the deviation is beyond a crazy threshold, the calibration -//! will fail, and then we will fall back to coarse time. +//! Time Stamp Counter (TSC) doesn't necessarily tick in constant speed and even doesn't synchronize +//! across CPU cores. The calibration detects the TSC deviation and calculates the correction +//! factors with the assistance of a source wall clock. Once the deviation is beyond a crazy +//! threshold, the calibration will fail, and then we will fall back to coarse time. //! -//! This calibration is stored globally and reused. In order to start the calibration before any call to `minstant` -//! as to make sure that the time spent on `minstant` is constant, we link the calibration into application's -//! initialization linker section, so it'll get executed once the process starts. +//! This calibration is stored globally and reused. In order to start the calibration before any +//! call to `fastant` as to make sure that the time spent on `fastant` is constant, we link the +//! calibration into application's initialization linker section, so it'll get executed once the +//! process starts. //! -//! *[See also the `Instant` type](crate::Instant).* - -#![cfg_attr(docsrs, feature(doc_cfg))] +//! **[See also the `Instant` type](Instant).** mod instant; #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))] mod tsc_now; +pub use instant::Anchor; #[cfg(all(feature = "atomic", target_has_atomic = "64"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "atomic", target_has_atomic = "64"))))] pub use instant::Atomic; -pub use instant::{Anchor, Instant}; +pub use instant::Instant; -/// Return `true` if the current platform supports [TSC](https://en.wikipedia.org/wiki/Time_Stamp_Counter), -/// and the calibration has succeed. +/// Return `true` if the current platform supports Time Stamp Counter (TSC), +/// and the calibration has succeeded. /// /// The result is always the same during the lifetime of the application process. #[inline] @@ -102,11 +99,14 @@ pub(crate) fn nanos_per_cycle() -> f64 { #[cfg(test)] mod tests { - use super::*; + use std::time::Duration; + use std::time::Instant as StdInstant; + use rand::Rng; - use std::time::{Duration, Instant as StdInstant}; use wasm_bindgen_test::wasm_bindgen_test; + use super::*; + #[test] #[wasm_bindgen_test] fn test_is_tsc_available() { @@ -147,7 +147,7 @@ mod tests { let std_instant = StdInstant::now(); std::thread::sleep(Duration::from_millis(rng.gen_range(100..500))); let check = move || { - let duration_ns_minstant = instant.elapsed(); + let duration_ns_fastant = instant.elapsed(); let duration_ns_std = std_instant.elapsed(); #[cfg(target_os = "windows")] @@ -156,7 +156,7 @@ mod tests { let expect_max_delta_ns = 5_000_000; let real_delta = (duration_ns_std.as_nanos() as i128 - - duration_ns_minstant.as_nanos() as i128) + - duration_ns_fastant.as_nanos() as i128) .abs(); assert!( real_delta < expect_max_delta_ns, @@ -165,7 +165,9 @@ mod tests { ); }; check(); - std::thread::spawn(check).join().expect("join failed"); + std::thread::spawn(check) + .join() + .expect("failed to join thread"); } } } diff --git a/src/tsc_now.rs b/src/tsc_now.rs index c811d02..220c82d 100644 --- a/src/tsc_now.rs +++ b/src/tsc_now.rs @@ -2,8 +2,9 @@ //! This module will be compiled when it's either linux_x86 or linux_x86_64. +use std::cell::UnsafeCell; +use std::fs::read_to_string; use std::time::Instant; -use std::{cell::UnsafeCell, fs::read_to_string}; static TSC_STATE: TSCState = TSCState { is_tsc_available: UnsafeCell::new(false),