mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-07 00:00:09 +02:00
time: Improve Instant::now() perf with test-util (#5513)
The test-util feature flag is only intended to be used with tests. However, it is possible to enable it in release mode accidentally. This patch reduces the overhead of `Instant::now()` when the `test-util` feature flag is enabled but `time::pause()` is not called. The optimization is implemented by adding a static atomic flag that tracks if `time::pause()` has ever been called. In `Instant::now()`, the atomic flag is first checked before the thread-local and mutex are accessed.
This commit is contained in:
@@ -4,6 +4,9 @@ version = "0.0.0"
|
||||
publish = false
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
test-util = ["tokio/test-util"]
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.5.0", path = "../tokio", features = ["full"] }
|
||||
bencher = "0.1.5"
|
||||
@@ -67,3 +70,8 @@ harness = false
|
||||
name = "copy"
|
||||
path = "copy.rs"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "time_now"
|
||||
path = "time_now.rs"
|
||||
harness = false
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
//! Benchmark spawning a task onto the basic and threaded Tokio executors.
|
||||
//! This essentially measure the time to enqueue a task in the local and remote
|
||||
//! case.
|
||||
|
||||
#[macro_use]
|
||||
extern crate bencher;
|
||||
|
||||
use bencher::{black_box, Bencher};
|
||||
|
||||
fn time_now_current_thread(bench: &mut Bencher) {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_time()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
bench.iter(|| {
|
||||
rt.block_on(async {
|
||||
black_box(tokio::time::Instant::now());
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
bencher::benchmark_group!(time_now, time_now_current_thread,);
|
||||
|
||||
bencher::benchmark_main!(time_now);
|
||||
@@ -30,6 +30,8 @@ cfg_not_test_util! {
|
||||
cfg_test_util! {
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::loom::sync::Mutex;
|
||||
use crate::loom::sync::atomic::Ordering;
|
||||
use std::sync::atomic::AtomicBool as StdAtomicBool;
|
||||
|
||||
cfg_rt! {
|
||||
#[track_caller]
|
||||
@@ -65,6 +67,15 @@ cfg_test_util! {
|
||||
inner: Mutex<Inner>,
|
||||
}
|
||||
|
||||
// Used to track if the clock was ever paused. This is an optimization to
|
||||
// avoid touching the mutex if `test-util` was accidentally enabled in
|
||||
// release mode.
|
||||
//
|
||||
// A static is used so we can avoid accessing the thread-local as well. The
|
||||
// `std` AtomicBool is used directly because loom does not support static
|
||||
// atomics.
|
||||
static DID_PAUSE_CLOCK: StdAtomicBool = StdAtomicBool::new(false);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
/// True if the ability to pause time is enabled.
|
||||
@@ -199,6 +210,10 @@ cfg_test_util! {
|
||||
|
||||
/// Returns the current instant, factoring in frozen time.
|
||||
pub(crate) fn now() -> Instant {
|
||||
if !DID_PAUSE_CLOCK.load(Ordering::Acquire) {
|
||||
return Instant::from_std(std::time::Instant::now());
|
||||
}
|
||||
|
||||
with_clock(|maybe_clock| {
|
||||
Ok(if let Some(clock) = maybe_clock {
|
||||
clock.now()
|
||||
@@ -241,6 +256,9 @@ cfg_test_util! {
|
||||
This is the default Runtime used by `#[tokio::test].");
|
||||
}
|
||||
|
||||
// Track that we paused the clock
|
||||
DID_PAUSE_CLOCK.store(true, Ordering::Release);
|
||||
|
||||
let elapsed = match inner.unfrozen.as_ref() {
|
||||
Some(v) => v.elapsed(),
|
||||
None => return Err("time is already frozen")
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#![cfg(all(feature = "full"))]
|
||||
|
||||
use tokio::time::{Duration, Instant};
|
||||
|
||||
#[tokio::test(start_paused = true)]
|
||||
async fn test_start_paused() {
|
||||
let now = Instant::now();
|
||||
|
||||
// Pause a few times w/ std sleep and ensure `now` stays the same
|
||||
for _ in 0..5 {
|
||||
std::thread::sleep(Duration::from_millis(1));
|
||||
assert_eq!(now, Instant::now());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user