Replies: 3 comments 1 reply
-
The tokio-metric metric is mean_scheduled_duration. |
Beta Was this translation helpful? Give feedback.
-
Tasks sent to external runtimes go through the global queue, whereas it goes to the local queue when called on a runtime thread. |
Beta Was this translation helpful? Give feedback.
-
Update / Further Investigation: I’ve made some additional observations. In the production environment, rt2 is a current-thread runtime. However, I noticed that the number of remote schedules is significantly higher than local schedules, which seems unusual. To investigate further, I created a minimal demo to try and reproduce the issue. However, in the demo environment, everything behaves as expected: the number of local schedules is much higher than remote schedules. This discrepancy between the demo and production environment is puzzling. Any ideas on what might be causing this behavior? use anyhow::Result;
use tokio::io::AsyncWriteExt;
fn main() -> Result<()> {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?;
let handle = rt.handle().clone();
let (tx, rx) = tokio::sync::oneshot::channel::<()>();
let thread = std::thread::spawn(move || {
let _ = rt.block_on(rx);
});
let main_rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(2)
.enable_all()
.build()?;
// spawn some tasks
main_rt.block_on(async move {
tokio::spawn(async move {
let mut handlers = Vec::new();
for i in 0..10 {
let mut new_tcp = tokio::net::TcpStream::connect("127.0.0.1:40048")
.await
.unwrap();
let h = handle.spawn(async move {
loop {
println!("task {} is running", i);
// simulate a data transfer with the tcp established by main_rt
let _ = new_tcp.write_all(&[0; 1024]).await;
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
println!(
"remote_schedule_count: {}",
tokio::runtime::Handle::current()
.metrics()
.remote_schedule_count()
);
for i in 0..tokio::runtime::Handle::current().metrics().num_workers() {
println!(
"local_schedule_count worker {}: {}",
i,
tokio::runtime::Handle::current()
.metrics()
.worker_local_schedule_count(i)
);
}
}
return Ok::<_, ()>(new_tcp);
});
handlers.push(h);
}
for h in handlers {
let _ = h.await;
}
});
tokio::time::sleep(tokio::time::Duration::from_secs(100)).await;
});
let _ = thread.join();
Ok(())
}
output:
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I’ve created two Tokio runtimes: rt1 and rt2.
In my code, a task is spawned from rt1 onto rt2 like this:
However, I’ve noticed a performance issue:
After running multiple tests, I observed that if rt2 is just a handle to the current runtime (i.e., let rt2 = tokio::runtime::Handle::current();, effectively using rt1), the scheduling latency measured by tokio-metrics is significantly lower.
In contrast, when rt2 is a separate current-thread runtime running on a different system thread or multi-thread runtime, the scheduling latency increases noticeably.
Is this expected behavior? Or could this indicate an inefficiency in how Tokio handles cross-runtime task scheduling?
Any insights would be appreciated!
metric:

Beta Was this translation helpful? Give feedback.
All reactions