异步运行时移动 Futures

问题描述 投票:0回答:0

我有我的异步函数:

async fn work<const BUFFER_SIZE: usize>() {
    let mut buffer = [MaybeUninit::<u8>::uninit(); BUFFER_SIZE]; // Big chunk stack memory
    
    yield_now().await;  // Force flow control back, save the buffer state
    
    buffer[0] = MaybeUninit::new(0x42);  // use the buffer later when resuming from the await point
}

脱糖函数返回的

impl Future
表示包含 buffer
FSM
执行,因为状态需要跨等待点保存。

所以我最终得到了一个

Future
对象,其内存布局大小约为
BUFFER_SIZE
字节。这没什么大不了的:
buffer
初始化(以及
Future
构造)是一个简单而快速的堆栈分配(只是一个
sub rsp, BUFFER_SIZE
汇编指令,甚至没有初始化)。

但是,当需要在内存中移动对象时,拥有大堆栈内存占用对象就变得相关(至少在我的情况下)。整个内存布局是

memmove
位于新位置(因此,对于
buffer
的每次移动都会复制整个
Future
)。

看起来(使用

tokio
glib-rs
进行测试)这些运行时确实在执行/轮询期间不断移动
Future
内存。

对执行该

Future
的几个任务使用perf执行,结果显示大部分时间都花在
memmove
上。 而且,我用一些基准进行了测试,很明显,处理
Future
的时间随着缓冲区的大小而增长,即
O(BUFFER_SIZE)
(注意缓冲区初始化不涉及归零操作)。

我知道,通常人们希望保持

Future
尽可能小,并且通过堆分配(例如,
Vec
),移动变得便宜。然而,这更像是一个玩具示例,我对解决方案不感兴趣,而是对其背后的原因感兴趣。

特别是:

  1. 内存不是不允许移动吗,因为一旦
    Pin
    ned(用于轮询),
    Future
    就无法取消固定(
    !Unpin
    )?
  2. (如果存在通用答案)为什么运行时实现需要移动
    Future
rust async-await future rust-tokio
© www.soinside.com 2019 - 2024. All rights reserved.