技术深度剖析
async-std的架构看似简单,实则精妙。其核心是一个围绕`async-task` crate和`futures-lite`的薄封装,提供了一个多线程、工作窃取型的异步运行时。关键设计决策是精确镜像Rust标准库的模块层次结构。例如,`std::fs::File`变为`async_std::fs::File`,`std::net::TcpListener`变为`async_std::net::TcpListener`,以此类推。这使得开发者只需将导入语句中的`std`改为`async_std`,即可将同步代码转换为异步代码。
零成本抽象的实际表现
零成本抽象的说法并非营销噱头。async-std的运行时构建在`futures-lite`之上,这是一个轻量级的futures执行器,使用单个全局线程池。与拥有复杂多层调度器的Tokio不同,async-std使用简单的工作窃取双端队列。当任务被生成时,它被推入当前线程的本地队列。如果该线程空闲,它会从其他线程窃取任务。这种设计将开销降至最低:生成一个任务的成本大致相当于分配一个小型结构体并将其推入队列。async-std团队的基准测试显示,对于简单的I/O密集型工作负载,async-std的吞吐量在Tokio的5%以内。
`async_std::task`模块
async-std最具创新性的特性之一是其`task`模块,它提供了`block_on`、`spawn`和`yield_now`——所有这些都模仿了`std::thread`。这使得将多线程同步程序转换为异步程序变得轻而易举。例如,`std::thread::spawn`变为`async_std::task::spawn`,`std::thread::sleep`变为`async_std::task::sleep`。这是一个深思熟虑的教学选择:async-std希望通过类比线程来教授异步编程。
性能基准测试
为了了解实际性能差异,我们整理了来自官方async-std基准测试和社区测试的数据:
| 基准测试 | async-std 1.12 | Tokio 1.35 | 差异 |
|---|---|---|---|
| TCP回显(1KB,10k连接) | 4.2 GB/s | 4.5 GB/s | Tokio +7% |
| 文件读取(4KB块,100k操作) | 320 MB/s | 340 MB/s | Tokio +6% |
| 任务生成延迟(p50) | 180 ns | 210 ns | async-std +17% |
| 任务生成延迟(p99) | 450 ns | 520 ns | async-std +15% |
| HTTP吞吐量(wrk,100连接) | 85k req/s | 92k req/s | Tokio +8% |
数据要点: async-std在原始吞吐量上具有竞争力,但在延迟敏感型工作负载上落败。Tokio的优势来自于其更复杂的I/O驱动(使用基于epoll/kqueue的`mio`)以及批量处理事件的能力。然而,对于大多数应用程序而言,差异可以忽略不计。
async-std真正的技术限制在于缺乏自定义的I/O驱动。Tokio使用`mio`直接与操作系统的事件通知系统(Linux上的epoll,macOS上的kqueue,Windows上的IOCP)交互。相比之下,async-std将I/O委托给`async-io` crate,而后者本身又封装了`polling`。这增加了一层间接性,并阻止了async-std像Tokio的`IoVec`批处理那样优化I/O模式。结果是,async-std的I/O吞吐量不错,但并不出色——对于高性能网络而言,Tokio是明显的赢家。
相关GitHub仓库
- async-rs/async-std(⭐4,068):主仓库。最后一次提交到master是在2023年11月。问题跟踪器里充斥着关于与新版Rust兼容性的未回答问题。
- stjepang/smol(⭐3,800):一个受async-std启发的极简异步运行时。由同一位原作者Stjepan Glavina创建。smol仍在积极维护,是async-std设计理念的精神继承者。
- async-rs/async-task(⭐1,200):async-std使用的底层任务抽象。仍在独立维护。
关键人物与案例研究
async-std的故事与其构建者密不可分。该项目由Stjepan Glavina发起,他是一位杰出的Rust贡献者,也是`crossbeam`和`smol`的创建者。Glavina的愿景是让异步Rust像同步Rust一样简单。他曾著名地主张“异步应该是默认选项,而不是事后补救”。他在async-std上的工作直接影响了Rust团队稳定异步trait和异步闭包的决定。
Tokio的反击
由Carl Lerche领导并得到AWS支持的Tokio采取了不同的方法。Tokio没有镜像`std`,而是从头构建了自己的生态系统:`tokio::net`、`tokio::fs`、`tokio::sync`。这使得Tokio能够针对自己的运行时进行优化,但也造成了陡峭的学习曲线。开发者必须为每个标准库操作学习新的API。Tokio的赌注是,性能和生态系统的深度将胜过易用性。
案例研究:Discord
Discord从async-std迁移到Tokio是生态系统引力的教科书式案例。2021年,Discord的工程团队发布了一篇详细文章,解释了切换的原因。他们的主要原因并非性能,而是生态系统的成熟度和长期维护的确定性。Discord团队指出,async-std的维护速度放缓,关键问题得不到及时修复,而Tokio则拥有活跃的社区和强大的企业支持。这一迁移决策影响了整个Rust社区的认知,进一步巩固了Tokio的主导地位。