I am new to Rust, now learning the async/await part. While trying to compare the implementation with other languages, I am wondering the performance characteristics of a specific pattern.
Consider an example with the following pattern:
async fn async_future1() {
async_future2().await
}
async fn async_future2() {
async_future3().await
}
...
async fn async_future_n_minus_1() {
async_future_n().await
}
async fn async_future_n() {
for i in 0..m {
io_future().await;
}
}
According to the executor implementation demonstrated here: Applied: Build an Executor - Asynchronous Programming in Rust. My understanding about the execution flow is like that:
Iteration 1:
future1.poll(...)
-> future2.poll(...)
-> future3.poll(...)
, ..., -> future_n.poll(...)
-> io_future().poll(...)
. Eventually, Poll::Pending
is returned from io_future().poll(...)
Here future_i
is the impl Future<Output = ()>
type generated from async_future_i
by the compiler.
Iteration 2:
Invoked when the task is woken up. Since only the entry Future
: future1
is registered to the Task
, the actual invocation again starts with future1.poll(...)
, which leads to a similar calling chain as Iteration 1. Eventually, Poll::Pending
is returned from io_future().poll(...)
Iteration 3:
Similarly, n
future_i.poll()
are called.
...
Iteration m:
Similarly, n
future_i.poll()
are called.
Thus, it seems to be that: in this pattern, the time complexity to complete async_future1()
is O(n * m)
. That is, as long as the invocation chain of poll
method is deep enough, the performance will degenerate.
I am not sure if I understand the implementation incorrectly, or there is any optimization done by the compiler to avoid the problem, or there is any optimization planned for the future?
Really appreciate Rust experts could provide insights regarding this!