Skip to content

Commit 2e80400

Browse files
authoredApr 17, 2024··
Avoid race condition in crutest rand-read/write (#1261)
Previously, the IO tasks could stop before the stat-gathering main loop, so you could get nonsensical readings like ``` 713.4 MiB/sec ± 14.2 MiB/sec (713 IOPS) 683.6 MiB/sec ± 11.2 MiB/sec (683 IOPS) 698.8 MiB/sec ± 409.6 KiB/sec (698 IOPS) 722.2 MiB/sec ± 10.2 MiB/sec (722 IOPS) 674.4 MiB/sec ± 13.6 MiB/sec (674 IOPS) 737 MiB/sec ± 5.8 MiB/sec (737 IOPS) 709.6 MiB/sec ± 16.8 MiB/sec (709 IOPS) 728.6 MiB/sec ± 10.6 MiB/sec (728 IOPS) 723.6 MiB/sec ± 15.2 MiB/sec (723 IOPS) 694.6 MiB/sec ± 8.6 MiB/sec (694 IOPS) 713.2 MiB/sec ± 7.2 MiB/sec (713 IOPS) stopping guest [0 jobs remaining] ---------------------------------------------- average read performance: 650.1 MiB/sec ± 196.6 MiB/sec (650 IOPS) ``` Notice the average read performance is nowhere near the average values reported during runtime! This is because the last two IO samples are captured as the system is shutting down. Here are the raw samples: ``` [733164339.2, 762943897.6, 728550604.8, 705062502.4, 732325478.4, 733164339.2, 746586112.0, 767977062.4, 692899020.8, 721420288.0, 778882252.8, 766718771.2, 726453452.8, 761685606.4, 775107379.2, 752877568.0, 742811238.4, 774687948.8, 719323136.0, 737358643.2, 755394150.4, 740294656.0, 3774873.6, 0.0] ```
1 parent 59bd303 commit 2e80400

File tree

1 file changed

+13
-21
lines changed

1 file changed

+13
-21
lines changed
 

‎crutest/src/main.rs

+13-21
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
use std::fs::File;
33
use std::io::Write;
44
use std::net::{IpAddr, SocketAddr};
5+
use std::num::NonZeroU64;
56
use std::path::{Path, PathBuf};
67
use std::sync::{
78
atomic::{AtomicBool, AtomicUsize, Ordering},
@@ -308,8 +309,8 @@ struct RandReadWriteWorkload {
308309
#[clap(long, default_value_t = 1.0)]
309310
sample_time: f64,
310311
/// Number of subsamples per sample
311-
#[clap(long, default_value_t = 10)]
312-
subsample_count: u64,
312+
#[clap(long, default_value_t = NonZeroU64::new(10).unwrap())]
313+
subsample_count: NonZeroU64,
313314
}
314315

315316
/// Mode flags for `rand_write_read_workload`
@@ -334,7 +335,7 @@ struct RandReadWriteConfig {
334335
/// Rate at which we should print samples
335336
sample_time_secs: f64,
336337
/// Number of subsamples for each `sample_time_secs`, for standard deviation
337-
subsample_count: u64,
338+
subsample_count: NonZeroU64,
338339
fill: bool,
339340
}
340341

@@ -2493,43 +2494,34 @@ async fn rand_read_write_workload(
24932494
workers.push(handle);
24942495
}
24952496

2496-
// Spawn a task which stops us after the given time
2497-
{
2498-
let stop = stop.clone();
2499-
let time_secs = cfg.time_secs;
2500-
tokio::task::spawn(async move {
2501-
tokio::time::sleep(Duration::from_secs(time_secs)).await;
2502-
stop.store(true, Ordering::Release);
2503-
});
2504-
}
2505-
25062497
// Initial sleep
25072498
let mut prev = 0;
25082499

25092500
let subsample_delay = Duration::from_secs_f64(
2510-
cfg.sample_time_secs / cfg.subsample_count as f64,
2501+
cfg.sample_time_secs / cfg.subsample_count.get() as f64,
25112502
);
25122503
let mut samples = vec![];
25132504
let mut first = true;
2514-
let mut next_time = std::time::Instant::now() + subsample_delay;
2515-
while !stop.load(Ordering::Relaxed) {
2505+
let mut next_time = Instant::now() + subsample_delay;
2506+
let stop_time = Instant::now() + Duration::from_secs(cfg.time_secs);
2507+
'outer: loop {
25162508
// Store speeds in bytes/sec, correcting for our sub-second sample time
25172509
let start = samples.len();
2518-
for _ in 0..cfg.subsample_count {
2519-
tokio::time::sleep_until(next_time.into()).await;
2510+
for _ in 0..cfg.subsample_count.get() {
2511+
tokio::time::sleep_until(next_time).await;
25202512
let bytes = byte_count.load(Ordering::Acquire);
25212513
next_time += subsample_delay;
25222514
samples.push((bytes - prev) as f64 / subsample_delay.as_secs_f64());
25232515
prev = bytes;
2516+
if Instant::now() >= stop_time {
2517+
break 'outer;
2518+
}
25242519
}
25252520
// Ignore the first round of samples, to reduce startup effects
25262521
if std::mem::take(&mut first) {
25272522
samples.clear();
25282523
continue;
25292524
}
2530-
if stop.load(Ordering::Relaxed) {
2531-
break;
2532-
}
25332525
let slice = &samples[start..];
25342526
let mean = slice.iter().sum::<f64>() / slice.len() as f64;
25352527
let stdev = (slice.iter().map(|&d| (d - mean).powi(2)).sum::<f64>()

0 commit comments

Comments
 (0)
Please sign in to comment.