内容提要
作者用Rust重写交叉编码器推理库cross-encode-rs,并与Python的fastembed和sentence-transformers对比。三者底层均使用onnxruntime,小模型上性能接近,Rust仅快约1.1倍;大模型上Rust快约1.4倍,模型加载时间相当。结论是底层运行时比编程语言更重要,但Rust在部署和并发控制上仍有优势。
延伸解读
性能差异的根源:运行时而非语言
文章指出,三个库底层都使用onnxruntime,因此小模型上性能接近,Rust仅快约1.1倍。大模型上Rust快约1.4倍,这源于库如何配置和驱动运行时,而非语言本身。这提醒我们,在推理场景中,底层引擎的选择和调优往往比编程语言更能决定性能。
Rust的部署优势:无Python运行时与并发控制
尽管推理延迟优势有限,但Rust在部署上仍有价值。文章提到,Rust二进制无Python运行时和GIL,批处理工作线程可精细控制,便于构建高并发下保持快速的服务。此外,冷启动时Python解释器启动和导入库会带来额外开销,而Rust无此负担。
模型加载时间:语言影响甚微
在仅计时ONNX会话创建时,fastembed在MiniLM上略快约4毫秒,在Jina上两者几乎持平。这是因为两者都调用相同的onnxruntime函数,语言难以产生影响。但若计时整个进程,Python因解释器启动和导入库会多出约134毫秒,这是Python服务每次冷启动都要付出的代价。
配置的重要性:执行提供程序的影响
文章提到,sentence-transformers在未固定CPU执行提供程序时慢数倍,固定后性能才与其他库接近。这表明,即使使用相同的运行时,库的默认配置也可能显著影响性能。在实际部署中,检查并优化执行提供程序等配置,可能比更换编程语言带来更大的收益。
Q&A
用Rust重写交叉编码器推理库cross-encode-rs,相比Python的fastembed和sentence-transformers,性能提升有多大?
在小模型MiniLM上,cross-encode-rs仅比Python库快约1.1倍(中位数),p99快1.4倍;在大模型Jina上,快约1.3到1.5倍(中位数1.4倍)。模型加载时间两者相当。
为什么Rust重写的交叉编码器推理速度没有比Python快很多?
因为三个库底层都使用同一个C++推理引擎onnxruntime,大部分时间花在相同的编译代码上,所以性能接近。语言本身的影响较小,底层运行时更重要。
cross-encode-rs在哪些场景下比Python库更有优势?
在部署和并发控制上更有优势:单一二进制文件无需Python运行时,没有GIL限制,可以精确控制批处理线程,适合构建高并发下保持快速的独立服务。
交叉编码器(cross-encoder)和嵌入模型(bi-encoder)有什么区别?
交叉编码器将查询和文档一起输入模型,直接输出相关性分数,更准确但更昂贵;嵌入模型分别将查询和文档转为向量再比较,可以预先计算,但精度较低。
cross-encode-rs在推理时做了哪些优化?
主要优化是批处理:所有文档一次性编码,并按token长度排序分批,减少填充浪费;还使用了新发布的tokenizers v1,比v0.x略有加速。
模型加载时间方面,Rust和Python库相比如何?
仅比较ONNX会话创建时间,两者几乎相同:MiniLM上fastembed快约4毫秒(12%),Jina上基本持平。但整个进程启动时间,Python因解释器启动和导入库多出约134毫秒。