出于好奇,我编写了以下程序。它每秒滴答 X 次,它应该非常接近目标滴答计数,但不知怎的,它总是落后很多。
例如,对于 2 秒基准测试,输出为(应为 2*64 -> 128)。在发布模式下构建并不能解决这个问题:
Starting benchmark 2s with length
Benchmark started.
Ticked 83 in 2 seconds
其他长度也是一样的,例如 5 秒产生 196,应该是 320。实际上应该更多,因为我让它运行多一点。
是因为
Arc Mutex
慢,还是还有其他原因?瓶颈在哪里?
use std::{ time::{ SystemTime, UNIX_EPOCH }, sync::Arc };
use tokio::{ time::{ sleep, Duration }, sync::Mutex };
const TICK_COUNT: u64 = 64;
const BENCHMARK_LENGTH: u64 = 2;
#[tokio::main]
async fn main() {
let tick_count = 0;
let interval = 1000 / TICK_COUNT;
let shared = Arc::new(Mutex::new(tick_count));
let start_time = SystemTime::now();
println!("Starting benchmark {}s with length", BENCHMARK_LENGTH);
let displayed = shared.clone();
let incrementer = shared.clone();
tokio::spawn(async move {
loop {
if
start_time.duration_since(UNIX_EPOCH).unwrap() +
Duration::from_secs(BENCHMARK_LENGTH) <
SystemTime::now().duration_since(UNIX_EPOCH).unwrap()
{
println!("Ticked {} in {} seconds", displayed.lock().await, BENCHMARK_LENGTH);
std::process::exit(0);
}
sleep(Duration::from_millis(500)).await;
}
});
tokio::spawn(async move {
loop {
sleep(Duration::from_millis(interval)).await;
*incrementer.lock().await += 1;
}
});
let _ = tokio::spawn(async move {
println!("Benchmark started.");
loop {
sleep(Duration::from_secs(1)).await;
}
}).await;
}
我已经在我的计算机上尝试了你的程序。 复制
*incrementer.lock().await += 1;
语句不会改变行为(计数只是简单地相乘),因此速度减慢不是由于 Mutex
造成的。
此外,我将 Mutex
替换为原子整数,行为仍然相同。
cargo flamegraph
报告在下面的系统调用中花费了大量时间ThreadParker::futex_wait()
(parking_lot
),这证实了我在评论中表达的直觉:大部分时间都花在操作系统挂起/恢复底层上tokio
运行时的线程。
关于数值,我在2秒内获得了122个刻度,与预期的128相差不远。 使用 128
TICK_COUNT
,我在 2 秒内获得 240,而不是 256;使用 256 TICK_COUNT
,我在 2 秒内获得 466,而不是 512。
这并不理想,但与预期值相差不远。
也许你的操作系统不像我的那样反应灵敏? (我用的是很普通的Linux)