The rust code is really checking how big Tokio's structures that track timers are. Solving the problem in a fully degenerate manner, the following code runs correct correctly and uses only 35MB peak. 35 bytes per future seems pretty small. 1 billion futures was ~14GB and ran fine.
#[tokio::main]
async fn main() {
let sleep = SleepUntil {
end: Instant::now() + Duration::from_secs(10),
};
let timers: Vec = iter::repeat_n(sleep, 1_000_000_0).collect();
for sleep in timers {
sleep.await;
}
}
#[derive(Clone)]
struct SleepUntil {
end: Instant,
}
impl Future for SleepUntil {
type Output = ();
fn poll(self: Pin, cx: &mut Context) -> Poll {
if Instant::now() >= self.end {
Poll::Ready(())
} else {
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
Note: I do understand why this isn't good code, and why it solves a subtly different problem than posed (the sleep is cloned, including the deadline, so every timer is the same).The point I'm making here is that synthetic benchmarks often measure something which doesn't help much. While the above is really degenerate, it shares the same problems as the article's code (it just leans into problems much harder).