blob: 1de1b97c0ff3ffc6164763071f967aee816f3211 [file] [log] [blame]
#![feature(test)]
extern crate test;
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll, Waker};
use crossbeam::sync::Parker;
use test::Bencher;
/// Runs a future to completion on the current thread.
fn block_on<F: Future>(future: F) -> F::Output {
// Pin the future on the stack.
futures::pin_mut!(future);
thread_local! {
// Parker and waker associated with the current thread.
static CACHE: RefCell<(Parker, Waker)> = {
let parker = Parker::new();
let unparker = parker.unparker().clone();
let waker = async_task::waker_fn(move || unparker.unpark());
RefCell::new((parker, waker))
};
}
CACHE.with(|cache| {
// Panic if `block_on()` is called recursively.
let (parker, waker) = &mut *cache.try_borrow_mut().ok().expect("recursive `block_on`");
// Create the task context.
let cx = &mut Context::from_waker(&waker);
// Keep polling the future until completion.
loop {
match future.as_mut().poll(cx) {
Poll::Ready(output) => return output,
Poll::Pending => parker.park(),
}
}
})
}
#[bench]
fn custom_block_on_0_yields(b: &mut Bencher) {
b.iter(|| block_on(Yields(0)));
}
#[bench]
fn custom_block_on_10_yields(b: &mut Bencher) {
b.iter(|| block_on(Yields(10)));
}
#[bench]
fn custom_block_on_50_yields(b: &mut Bencher) {
b.iter(|| block_on(Yields(50)));
}
#[bench]
fn futures_block_on_0_yields(b: &mut Bencher) {
b.iter(|| futures::executor::block_on(Yields(0)));
}
#[bench]
fn futures_block_on_10_yields(b: &mut Bencher) {
b.iter(|| futures::executor::block_on(Yields(10)));
}
#[bench]
fn futures_block_on_50_yields(b: &mut Bencher) {
b.iter(|| futures::executor::block_on(Yields(50)));
}
struct Yields(u32);
impl Future for Yields {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.0 == 0 {
Poll::Ready(())
} else {
self.0 -= 1;
cx.waker().wake_by_ref();
Poll::Pending
}
}
}