Я пишу свой асинхронный движок. Я захотел предоставить пользователю более удобный интерфейс, для этого мне потребовалось дополнить его Future. Условно, он отдаёт мне Future, а я делаю что-то вроде
let mut res = MaybeUninit::uninit();
let res_ptr: *mut T = res.as_mut_ptr();
self.exec_future(async move {
unsafe { res_ptr.write(future.await) };
});
unsafe { res.assume_init() }
Попробовал так сделать, и обнаружил, что Future, который был создан, стоит в два раза больше. Я поставил несколько "экспериментов" и не обнаружил причин для этого.
Вот код для демонстрации (можете не запускать, я оставил комментарии после println)
let block= async {
let a = [0u8;8192];
yield_now().await;
black_box(println!("{}", a.len()));
0
};
#[inline(never)]
async fn a() -> usize {
let a = [0u8;8192];
yield_now().await;
black_box(println!("{}", a.len()));
0
}
let size_of_future_from_block = mem::size_of_val(&block);
println!("size_of_future_from_block: {}", size_of_future_from_block); // 8194
let size_of_future_from_fn = mem::size_of_val(&a());
println!("size_of_future_from_fn: {}", size_of_future_from_fn); // 8194
let closure_from_future_from_block = async {
block.await
};
let size_of_closure_from_future_from_block = mem::size_of_val(&closure_from_future_from_block);
println!("size_of_closure_from_future_from_block: {}", size_of_closure_from_future_from_block); // 16389
let closure_from_future_from_fn = async {
a().await
};
let size_of_closure_from_future_from_fn = mem::size_of_val(&closure_from_future_from_fn);
println!("size_of_closure_from_future_from_fn: {}", size_of_closure_from_future_from_fn); // 8195
Я запустил этот тест в release, если это важно.
UDP: я решил эту проблему для себя. Для этого достаточно создать вспомогательную структуру, которая реализует Future. Тогда память не будет удваиваться. Для случая выше, это могло выглядеть как:
struct WithRes<R, Fut: Future<Output=R>> {
res_ptr: *mut R,
fut: Fut
}
impl<R, Fut: Future<Output=R>> WithRes<R, Fut> {
fn new(res_ptr: *mut R, fut: Fut) -> Self {
Self { res_ptr, fut }
}
}
impl<R, Fut: Future<Output=R>> Future for WithRes<R, Fut> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = unsafe { self.get_unchecked_mut() };
let mut pinned = unsafe { Pin::new_unchecked(&mut this.fut) };
match pinned.as_mut().poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(res) => {
unsafe { this.res_ptr.write(res) };
Poll::Ready(())
}
}
}
}