最近半年,跟做AI基建的朋友聊技术选型,好几次都听到同一个变化:新写的底层组件,默认语言悄悄从C++/Go换成了Rust。两年前有人提“用Rust做AI基建”,我还会当成极客偏好,但最近看到的三个信号让我改变了判断——这已经不是小圈子里的尝鲜,而是整个AI基础设施栈正在发生的结构性转向。这篇文章就围绕这三个信号展开,聊聊背后发生了什么、为什么是这个时间点,以及它对做模型推理、数据管道、向量检索的工程师到底意味着什么。
1. 先搞清楚:AI基建到底是指哪一层
1.1 基建的定义:训练框架之上、业务模型之下
聊Rust转向之前,得先把“AI基建”这个词界定清楚。平时我们说的AI基建,通常不是指Transformer模型本身,也不是TensorFlow、PyTorch这类训练框架,而是训练框架之上、业务模型之下的中间层:数据清洗与特征工程管线、特征存储、在线推理服务、向量检索、模型调度器、监控与追踪系统、实验管理平台,等等。
这一层的特点是:它不直接决定模型的智能水平,但决定了模型能不能稳定、低成本、大规模地跑起来。业内有个很形象的比喻:模型是发动机,AI基建是底盘。发动机决定上限,底盘决定能不能安全上路。过去几年大家拼命卷模型,现在越来越多人发现,真正制约落地速度的,反而是底盘。
这一层在过去几年基本是Python、C++、Go三分天下。Python写业务逻辑和实验代码最顺手,C++负责算子和高性能推理,Go负责服务框架和调度系统。这套组合在小规模场景下没什么问题,可一旦推理集群跑到几十台甚至几百台GPU,数据管道每天处理TB级特征,三个语言各自的短板就藏不住了。
1.2 旧组合的痛点正在变得不可忽略
先说Python。作为胶水语言,它的开发效率确实是全场最高,但在AI基建的高频热点路径上,Python的GIL和解释器开销让吞吐上不去。很多团队为了性能,不得不把热点逻辑下沉到C++扩展,可这样一来,Python只是个壳,真正的复杂度全部压到了C++层。
再看C++。性能没得说,但这几年给推理服务写调度层、给特征存储写并发逻辑的同学,多少都有过这种经历:一个多线程状态下偶现的野指针问题,本地复现不了,线上偶尔崩一次,靠core dump慢慢猜。内存安全问题在低并发场景下是“小概率”,到了大规模分布式场景就成了“必然会发生的日常”。而定位这类问题的成本,往往比重写一遍代码还高。
至于Go,胜在并发模型简单,goroutine写起来很爽,GC也让内存管理省心。但在高吞吐、低延迟的AI推理路径上,GC引起的停顿是实打实的痛点。某次压测中,P99延迟本来已经压到了几十毫秒,结果GC一触发,延迟直接跳了一个数量级。这种不可预测的抖动,在在线推理场景里是很致命的。
说白了,AI基建正在从“能用”走向“要稳”。当系统规模大到一定程度,“内存安全”和“确定性性能”就不再是洁癖,而是硬需求。这也正是Rust会被认真考虑的根本背景。
2. 信号一:推理引擎与运行时层开始Rust化
2.1 为什么第一个突破口是“推理运行时”
我观察到的第一个明显信号,发生在推理引擎与运行时层。这一层离性能瓶颈最近,对稳定性的要求也最高,所以也最早感受到旧栈的痛点。
拿一个我实际接触过的案例来说。某公司新做了一个推理服务,早期技术栈是Python封装C++核心,动态批处理、KV Cache管理、请求调度都写在C++里。随着上下文窗口变大、并发请求数上去,C++层的并发问题开始频繁暴露:有时是多线程同时写KV Cache导致的内存错误,有时是调度器在极端情况下出现竞态条件。问题本身不难理解,但每次都要花很长时间定位,真正让团队头疼的是“复现困难”——线上偶发的问题,线下怎么压测都触发不了。
后来团队把核心调度层用Rust重写了一遍,效果很直观。调度延迟下来了,更关键的是,那一类内存安全问题几乎绝迹了。原因不复杂:Rust的所有权模型和Send/Sync约束,让数据竞争在编译期就被拦下了大半。只要代码能过编译,运行时出这类问题的概率就低了一个量级。
2.2 最容易落地的三块:tokenizer、调度器、前缀缓存
从众多团队的实践看,推理运行时里最先被Rust化的是这三块:
- Tokenizer与文本预处理:纯CPU密集、高频调用,逻辑相对独立,边界清晰,特别适合作为一个独立Rust组件嵌入现有系统。很多tokenizer库的Rust实现性能比Python绑定版本高出一截,而且内存占用稳定。
- 请求调度与动态批处理:这是并发复杂度最高的地方。请求来了要排队、要组batch、要分配显存和KV Cache空间。用Rust的异步运行时处理大量并发请求,配合无锁数据结构管理共享状态,代码写起来更安全,也更容易推理性能边界。
- 前缀缓存与KV Cache管理:高并发下多路请求共享相同前缀时,需要一套高效且线程安全的结构来复用缓存。这类结构在C++里要小心处理锁和生命周期,在Rust里通过类型系统能提前约束住很多非法访问。
这三块的共同点是:都在热点路径上、都对稳定性和性能有极高要求、逻辑边界比较清晰。换句话,它们是“适合Rust发挥的靶点”,而不是为了技术而技术。
2.3 实操示例:用PyO3把Rust组件嵌进Python栈
很多团队不会立刻把所有推理逻辑都迁到Rust,更现实的做法是:Python保留整体调度和业务逻辑,把热点函数用Rust重写,通过PyO3做成Python扩展。这个模式我强烈推荐想试水的团队先跑通一遍,成本低、见效快。
一个最简单的例子。假设我们有个高频调用的函数,需要批量计算一组文本的特征得分,用Rust实现核心逻辑,暴露给Python调用。Cargo.toml大致是这样:
[package] name = "fast-core" version = "0.1.0" edition = "2021" [lib] name = "fast_core" crate-type = ["cdylib"] [dependencies] pyo3 = { version = "0.22", features = ["extension-module"] }Rust侧实现一个pyfunction并注册到模块:
use pyo3::prelude::*; #[pyfunction] fn batch_scores(inputs: Vec<String>) -> Vec<f64> { inputs .iter() .map(|s| { // 这里换成真正的特征计算逻辑 let base = s.len() as f64; base * 0.618 }) .collect() } #[pymodule] fn fast_core(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(batch_scores, m)?)?; Ok(()) }构建先用maturin init初始化项目结构,然后maturin develop把扩展编译进当前Python环境。Python端调用时,跟调用普通函数一样:
import fast_core scores = fast_core.batch_scores(["hello", "world", "rust"]) print(scores)这里有个很重要的实践细节:FFI边界上的类型要尽量简单,传字符串数组、整数、浮点数这类可序列化类型,尽量避免在边界上传自定义复杂对象。频繁的小对象跨边界拷贝会吃掉性能收益,所以更好的做法是在Rust内部做批处理,一次调用处理一批数据。
提示:如果第一次接触PyO3,建议先用一个只有纯计算、不涉及Python对象生命周期的函数试水。先跑通构建和调用链路,再逐步把更复杂的逻辑放进去。这样排查问题时,不会把Rust错误和Python交互问题混在一起。
3. 信号二:数据管道与存储层的新选择
3.1 数据层的性能瓶颈往往比想象中更严重
第二个信号出现在数据管道与存储层。很多团队做AI基建时,一开始注意力都在模型推理上,直到数据量上来才发现,真正的瓶颈经常不在GPU,而在数据入口。
我见过一个比较典型的场景:某离线特征计算管道,早期是Python写的,分成多个步骤批量处理日志数据。当天数据量小的时候跑得挺顺畅,后来接入的新业务让日数据量翻了几倍,管道开始频繁超时。排查下来,问题出在多线程处理时的共享状态竞争上,Python的GIL让并行度上不去,还时不时碰到状态不一致。
这种困境在数据层很普遍。数据管道要做ETL清洗、特征计算、向量化写入、索引更新,这些工作天然适合并行处理,但又特别依赖“多线程同时操作共享数据时的安全性”。C++可以做,但线程安全要自己小心维护;Go也可以做,但GC在大内存多线程场景下会带来不可控的暂停。
3.2 Rust在数据层的四个核心优势
在数据层这个场景里,Rust有几个优势是实打实的:
第一,无GC、延迟可预测。数据管道组件通常常驻内存,GC暂停会导致整个处理链路出现不定期的“卡顿”。Rust没有GC,内存管理在编译期确定,运行时的延迟曲线稳定得多。
第二,多线程安全由编译器保障。用rayon这样的数据并行库,可以很方便地把一个大数据集切到多线程处理,而且不需要担心数据竞争。这个特性在数据并行场景里体验特别好——把集合调成par_iter(),并行度就上去了,安全性有编译器兜底。
第三,Arrow生态成熟。列式内存格式Arrow在Rust里有arrow-rs这种高质量实现,Parquet读写也有parquet-rs。对于特征存储和数据分析链路,直接用Arrow格式做数据交换,可以减少序列化开销。很多数据平台团队用Rust写读写Arrow格式的组件,配套路线已经相当成熟。
第四,做向量索引更安全。近年大热的向量检索里,HNSW这类索引结构有很多Rust实现,性能接近C++版本,但内存管理更省心。增量更新索引时,Rust的类型系统能拦住很多UAF(释放后使用)问题。
3.3 实践观察:向量索引与特征服务的迁移动线
我观察到的迁移动线,跟推理层不一样,数据层很少直接“推倒重来”,而是选两个点先切进去。
一个是高频访问的特征服务。这类服务对延迟极其敏感,同时需要在大内存里维护特征缓存。某团队的特征服务早期是Go写的,内存占用高、GC频繁导致P99抖动,后来把热点读路径用Rust重写,内存占用降了大约40%,P99延迟稳定了很多。另一个是吞吐瓶颈明显的ETL环节。某平台把离线特征计算中的一个核心聚合步骤从Python改成Rust实现,用rayon做数据并行,处理时间从分钟级降到了秒级,而且没有引入额外的基础设施。
这里给个很实用的建议:数据层不要一上来就Rust化整条管道,可以从两个切入点开始,一是高频读写的特征服务,二是单点吞吐瓶颈明显的ETL步骤。保留Python作为调度管理层,把热点路径Rust化,是最稳妥的过渡路径。
4. 信号三:开源生态与基础设施工具链的整体迁移
4.1 新项目默认语言变了
第三个信号,不是某个单独组件,而是整个生态层面的变化。最直观的一点:AI基建相关的新开源项目里,Rust的占比明显提高了。几年前Rust在这些项目里还是零星出现,更多是补充角色;现在很多新项目直接把Rust作为默认实现语言,立项时讨论的已经不是“要不要用Rust”,而是“哪些模块不适合用Rust”。
这种变化,强调一下,不是一家两家公司的选择,而是一种行业性的默认路径。我关注了几个新发布的推理运行时、向量数据库、特征存储相关项目,代码库的主体语言是Rust的越来越多。一个新项目选择语言时,会很大程度上参考前人的实践和社区积累,当“Rust做AI基建”的项目数量攒到某个临界点,后面的项目就会自然而然地跟上。
4.2 工具链成熟度:Rust在AI领域不再是“只有CLI”
另一个关键变化是Rust在AI领域的工具链已经成套了。前几年提起Rust,大多数场景还是写CLI工具或系统组件,在AI领域显得单薄。现在不一样了:
- 数据处理与存储: arrow-rs、parquet-rs、delta-rs这些项目让Rust能直接参与大数据链路。
- 推理相关: tokenizers、ort(ONNX Runtime的Rust绑定)、candle这类纯Rust推理框架、burn这个Rust深度学习框架,把模型加载、推理、训练的部分环节都覆盖了。
- 异步与调度: tokio已经是生产级异步运行时,很多推理服务的调度层直接建在tokio之上。
任何一个技术栈要成为基建级选型,不能只靠“某个组件性能好”,而是需要一整套生态:库、工具链、调试支持、可观测性集成,还有足够多的社区踩坑经验。Rust在过去两年恰好跨过了这个门槛。
4.3 团队和组织层面的变化信号
这个信号还体现在人才和组织层面。我注意到,一些公司AI平台团队的招聘JD里开始出现Rust相关要求,不是“加分项”,而是“必备项”。一些大型团队的内部技术评审里,“默认候选语言”从过去的Go/C++扩展到了Rust。还有些团队已经专门成立了Rust方向的专项小组,负责搭建内部公共库和最佳实践。
这些组织层面的变化,往往比技术层面的变化更能说明趋势。因为技术栈的切换成本很高,团队不会因为“某个语言很好”就轻易切换,只有当Rust真的解决了实际痛点,且社区生态足够支撑项目落地时,组织才会在人员配置和技能培养上做投入。
注意:面试造火箭,工作拧螺丝,这句调侃在Rust岗位上也适用。如果只是想要一个Rust写的脚本,挂在简历上好看,那不叫基建转向。真正的转向是团队愿意为一个新语言投入学习成本、愿意维护双语言技术栈,并且把这个语言用在关键路径上。
5. 为什么是Rust:技术根因与选型逻辑
5.1 性能与安全性:AI基建最硬的刚需
聊到这里,很多人会问:为什么偏偏是Rust而不是其他语言?其实答案就落在“性能”和“安全”这两个词上。
AI基建的热点路径,比如推理调度、特征读写、向量索引,需要的是极致性能;而大规模分布式环境下,内存安全问题会成倍放大。历史上,这两个需求很难同时被满足:C++有性能但安全性靠人肉保证,Java/Go有安全性但性能受GC影响。Rust最独特的地方在于,它把“安全”前置到了编译期。所有权机制和生命周期规则,让很多内存类Bug在写代码阶段就暴露了,而不是等到了线上再花几天排查。
有个很形象的类比:Rust的编译器就像一个极其严格的代码评审人,不让数据竞争、悬垂引用这些问题进入生产环境。代价是写代码时要多考虑一层“这个数据归谁所有”,换来的是运行时更高的确定性。对AI基建这种对稳定性要求极高的领域,这笔账算是算得过来的。
5.2 Rust、C++、Go、Python的同场对比
拿一张表来对比会直观一些。
| 维度 | C++ | Go | Rust | Python |
|---|---|---|---|---|
| 内存安全 | 手动管理,易出问题 | GC托管,相对安全 | 编译期保证,最稳 | 运行时托管 |
| 极致性能 | 好 | 较好 | 好 | 差 |
| 并发模型 | 手动线程,自由但易错 | goroutine,上手快 | Send/Sync + async,安全且可控 | GIL受限,并行度有限 |
| 延迟可预测性 | 高 | GC有抖动 | 高 | 差 |
| 开发效率 | 低 | 高 | 中等偏低 | 很高 |
| 与现有AI栈集成 | 强 | 中 | 强 | 作为胶水层 |
这张表可以解释为什么很多团队在推理运行时和存储层尝试Rust:它不是在所有维度上“全场最佳”,而是在“高性能+高安全”这个组合上,给了业界一个此前没有的选项。
5.3 什么场景不适合Rust
当然,Rust不是万能的。我踩过几次坑后,对“什么场景不适合Rust”有了比较清醒的认识。
如果你的团队没有Rust经验,业务逻辑又需要快速迭代、原型验证,这时候硬上Rust很可能会拖慢节奏。AI实验阶段的代码,追求的是“今天想到一个想法明天能验证”,Python依然是这个阶段的最佳选择。Rust适合的是已经验证过、需要规模化和稳定性的那部分逻辑,不是用来替代所有代码的。
另外,纯IO密集且性能不敏感的场景,比如偶尔跑一次的报表任务、简单的HTTP代理,用Rust收益并不明显,开发成本反而比Go更高。技术选型最忌讳“拿着锤子看什么都像钉子”,Rust再顺手,也要想清楚这个场景是不是真的需要一个高安全性的高性能语言。
6. 过渡期的实操经验与踩坑记录
6.1 常见的过渡策略:新组件先行
再聊聊怎么落地。从我观察到的成功案例看,几乎没有团队会直接喊“把所有系统重写为Rust”,最常见的过渡策略是“新组件先行”:新立项的组件,默认用Rust;存量系统保持不动,只在痛点明显的模块里做替换。
用Rust写一个新组件,比改一个老系统的风险低很多。新组件没有历史包袱,边界也清晰,出了问题影响面可控。比如新做一个模型监控Agent,或者一个指标采集器,这些小型服务特别适合当Rust的练兵场,既能验证团队的学习曲线,又能给后续大组件的迁移积累经验。
6.2 编译、异步、crate生态的真实体验
过渡期有几个坑,我提前说一下,遇到了不至于手忙脚乱。
第一个是编译时间。大型Rust项目的全量编译确实比Go慢不少,第一次从零拉取一个大型依赖树,全量编译几分钟到十几分钟都正常,这个体验会让习惯了“秒级编译”的同学很崩溃。解决思路是用sccache做编译缓存、把依赖拆分到workspace、CI里做增量缓存。实际用下来,配合好了编译时间可以压缩到可接受范围。
第二个是异步生态的复杂度。tokio本身很成熟,但Rust异步支持非同步代码有一些心智成本,尤其是要使用async trait(AFIT)的特性时,需要注意相关feature的版本情况。选第三方异步库时,要多看一眼维护状态和兼容文档,避免用到半死不活的老库。
第三个是crate质量参差。Rust生态整体健康,但毕竟年轻,同一类库有时会同时存在多个做法风格不同的实现。建议新项目一开始就引入cargo-audit做依赖安全检查,用cargo deny管理许可证和依赖来源。这个习惯越早养成越好,等依赖树大了再回头补,成本会高很多。
6.3 给团队的几条实操建议
如果团队准备认真尝试Rust,我有几条实操建议,都是亲测有效的:
第一,让团队先从一个内部CLI工具入手。比如写一个日志分析工具、一个配置文件校验器。CLI工具不涉及服务框架,也没有异步复杂度,适合先把crate组织、错误处理、测试这些基础打牢。
第二,从无async的小模块做起。先不要一上来就写一个基于tokio的高并发服务,先从纯计算模块开始,比如特征计算、数据格式转换,熟悉所有权和借用检查后,再挑战异步代码。
第三,至少保证团队里有1-2个已经有实际Rust项目经验的人。Rust的曲线比Go陡不少,如果全队都是新手,遇到借用检查器报错了没人能解释“为什么编译器不允许我这么做”,士气很容易崩。有经验的人能把团队拉过那个门槛。
我印象很深的另一个案例是某团队把模型监控Agent从Go迁到了Rust。最初只是嫌弃Go的Agent内存占用偏高,想试试Rust能不能降下去。结果迁移后不仅内存占用降了约40%,GC暂停直接消失,P99延迟也稳定了。这个案例说明,有些收益写代码之前都想象不到,真跑起来才知道值不值。
7. 影响范围拆解:对不同角色的实际影响
7.1 AI平台工程师
对做AI平台和基础设施的工程师来说,这个趋势最直接的影响是:Rust正在变成一个无法回避的技能方向。未来想去大公司做推理平台、特征平台、向量检索的人,简历上带Rust经验,肯定是一个不小的加分项。更重要的是,理解“用Rust解决AI基建痛点”的思维方式,比单纯会写Rust语法更能体现价值。
我认识的一些平台组工程师,一开始也未必是Rust高手,但他们做对了这样几件事:先在一个新组件上把Rust用于生产,然后复盘各模块性能和安全性的实际收益,再逐步扩大使用范围。这种渐进式的打法,既控制了风险,又积累了可复用的经验。
7.2 Python应用开发者
对大多数做模型应用的同学来说,短期内不需要把Rust作为必学语言。Python依然是AI应用层的主力,这点短时间内不会变。但架构视野上需要更新:未来的AI系统,很可能是“Python写业务、Rust写内核”的双层结构,就像前端写JavaScript、底层引擎越来越多其他高性能语言一样。Python开发者不需要自己写Rust,但要理解Rust组件的边界在哪里,知道怎么调用Native Extension,才能在系统性能出问题时,判断瓶颈是不是在Python层。
7.3 架构决策者
对于做技术决策的人,现在评估组件选型时,候选列表里已经不能只有Go和C++了。推理服务、向量检索、特征存储、数据管道,这些场景都可以认真评估Rust方案的可行性和成本。但同时也得冷静看待组织学习成本,如果团队完全没有Rust经验,又没有一个“试水”的小项目,就不要在大项目上冒险。合理路径是:先让一个小组件跑出数据上的收益,再让数据说服团队扩大投入。
7.4 对整个行业的技术栈走向
如果把这个趋势放到更大的背景里看,我觉得整个AI基建正在走向一个类似WebAssembly当年的路径:上层脚本语言负责快速迭代和表达力,底层用安全的高性能语言负责承载基础设施。过去是C++来做底层,现在是C++之外多了一个更安全的选项。区别在于,C++把安全责任交给开发者,Rust把安全责任交给编译器。在AI基建规模越来越大的当下,后者更符合行业对稳定性的要求。
我自己在实际项目中的体会是:Rust不会成为AI基建的唯一答案,但它大概率会成为高性能组件层的事实标准之一。现在依然有不少团队在用C++和Go维系整套系统,Rust更像是一个精准的“外科手术工具”,哪里痛、哪里对性能和安全要求最高,就往哪里用。这种“哪里痛哪里上”的务实路径,比“全面切换”更靠谱,也更符合大多数团队的实际情况。
最后再分享一个实用的小技巧:如果团队正在评估要不要引入Rust,可以先挑一个每周都会给你惹麻烦的小模块,比如频繁OOM的特征加载器、偶尔线程崩溃的调度组件,用Rust重写并跑上两周,把重构前后的P99延迟、内存占用、故障次数放到一张表里,数据会替你说话。技术选型这种事,与其争论语言优劣,不如让真实的压测数据来做决定。