写点什么

从 C 迁移到 Rust 的挑战与经验教训

作者:Sergio De Simone

  • 2024-11-18
    北京
  • 本文字数:1595 字

    阅读完需:约 5 分钟

从 C 迁移到 Rust 的挑战与经验教训

在一个系列文章中,Immunant 软件工程师 Stephen Crane 和 Khyber Sen 讲述了他们如何将互联网安全研究小组 (ISRG) 的 VideoLAN 和 FFmpeg AV1 解码器从 C 语言移植到 Rust 语言。该系列文章详细介绍了他们如何确保不出错并优化性能。


VideoLan VLC 和 FFMpeg 中使用的 AV1 解码器 dav1d 已经开发了六年多,包含大约 5 万行 C 代码和 25 万行汇编程序。正如 Crane 所说的那样,它成熟、速度快且应用广泛。因为代码高度优化,所以它的体积小、可移植性好、速度快。因此,他们坚持要移植,而不是使用 Rust 从头开始重写。


Immunant 的工程师们首先要做的选择是,是一步一步地进行移植,还是使用 c2rust 移植整个代码库,获得一个不安全但可运行的 Rust 实现,然后再以此为基础进行重构和重写,使其变得安全而又符合 Rust 的语言习惯。最终,他们决定采用 c2rust,因为它有两大优势:一是可以在重构的同时测试移植的代码,二是降低了对专家领域知识的要求。


我们发现,在重写和改进 Rust 代码的过程中,从一开始就进行全面的 CI 测试是非常有益的。我们可以对代码库进行横向修改,并在每次提交时运行已有的 dav1d 测试。[…] 该项目的大部分团队成员都是系统编程和 Rust 方面的专家,但之前并没有 AV 编解码器方面的经验。我们的编解码器专家 Frank Bossen 为项目提供了宝贵的指导,但大部分的工作他并不需直接要参。


将移植生成的 Rust 代码重构为安全、符合语言习惯的 Rust 代码面临着许多挑战,其中一些挑战与 C 和 Rust 之间的不匹配有关,例如生命周期管理(借用)、内存所有权、缓冲指针和联合体;另一些挑战则源于 dav1d 的设计,它非常依赖于对跨线程共享可变数据的访问。


通过使用MutexRwLock加锁,并在运行时使用Mutex::try_lock()RwLock::try_read()/RwLock:: try_write()进行验证,他们确保了线程可以访问数据而且又不会引入延迟,从而解决了与共享状态相关的线程安全问题。


这种方法可以很好地处理只有一个线程需要修改跨线程共享值的情况。然而,dav1d 还依赖于多个线程对单个缓冲区的并发访问,其中每个线程访问缓冲区的特定子区域。对此,Immunant 工程师并没有使用更符合 Rust 语言习惯的方法,即使用专门分配给不同线程的不相连的区域,而是创建了一个缓冲区封装类型DisjointMut,负责处理可变借用,并确保每一个都能独占访问。


另外两个颇具挑战性的领域是自引用结构(主要用于跟踪缓冲区位置的游标以及上下文结构之间的链接)和无标签联合体。由于 Rust 不允许使用自引用结构,所以游标指针被整数索引取代,而上下文结构之间的链接被取消,并通过函数参数进行引用。在适当的时候,无标签联合体会被转换为带标签的 Rust 联合体,而在其他情况下,zerocopy crate 会在运行时将相同的字节重新解释为两种不同的类型,以避免改变联合体的表示和大小。


移植的一个主要目标是保持性能不变。因此,Immunant 的工程师在每次提交的重构阶段都会仔细监控性能回归情况。在向安全代码转换的过程中,他们意识到,性能主要是受到一些微妙因素的影响,如动态分派汇编代码、边界检查和结构初始化的成本。最后,他们进行了与分支、内联和堆栈使用相关的更细致的优化。


性能优化工作显著降低了移植带来的开销,从 11% 降至 6%。按照 Crane 的说法,总体上,将 dav1d 移植到 rav1d 花费了三个开发人员 20 多个月的时间,所耗费的人工比最初预计的要多。但这也表明,将现有的 C 代码重写为安全、高性能的 Rust 代码并解决所有线程和借用难题是可能的。


对特别注重安全性的应用程序,rav1d 提供了一个内存安全的实现,而且不会因为沙箱等风险缓解措施而额外增加开支。我们相信,通过不断地优化和改进,在任何情况下,Rust 实现都可以与 C 语言实现相媲美,同时还能提供内存安全性。


他们从 rav1d 的诞生过程中学到的东西远不止这些,如果想了解更多信息,请阅读原文。


查看原文链接:

https://www.infoq.com/news/2024/10/porting-av1-decoder-rust/

2024-11-18 08:107174

评论

发布
暂无评论
发现更多内容

2023-11-18:用go语言,如果一个正方形矩阵上下对称并且左右对称,对称的意思是互为镜像, 那么称这个正方形矩阵叫做神奇矩阵。 比如 : 1 5 5 1 6 3 3 6 6 3 3 6 1 5

福大大架构师每日一题

福大大架构师每日一题

AWS EC2、阿里云ECS、腾讯云CVM初步对比

穿过生命散发芬芳

腾讯云 阿里云 AWS

jdk8 Stream流中将集合转成map,重复key处理,统计最大值,获取某个属性集合等10种最常用方法

javaNice

Java stream

DAPP代币燃烧铸币质押挖矿系统开发(源码搭建)

l8l259l3365

SQL 的 AND、OR 和 NOT 运算符:条件筛选的高级用法

小万哥

MySQL sql 程序员 后端 开发

openAi基于java jtokkit实现分词

智慧源点

jtokkit

一款带数字传输信号的OVP芯片

芯动大师

亚马逊Amazon OpenSearch Serverless"利刃在手,‘向量’八方"

淼.

连接亚马逊云EC2的几种方式

孤虹

Jenkins 打包shell出现gradle命令不存在,jvm内存溢出

javaNice

Java jenkins

目前为止BRC-20是炒作还是泡沫

币离海

BRC-20 铭文

Kubernetes成本优化

俞凡

Kubernetes

WordPress 基于亚马逊云的部署实践(一)- 架构设计

wangkx

AWS 亚马逊云 Amazon EC2 云服务器

亚马逊云EC2部署QNAP

孤虹

Unite for mac「将网站转换为应用程序」

加油,小妞!

Unite for Mac

「Macos好用的图片处理工具」Affinity Photo 最新中文版

加油,小妞!

图像处理 Affinity Photo

Redis缓存雪崩、击穿、穿透解释及解决方法,缓存预热,布隆过滤器 ,互斥锁

javaNice

Java redis

Python连接es笔记二之查询方式汇总

Hunter熊

Python elasticsearch Elasticsearch-dsl

你真的了解“有钱人”吗?

少油少糖八分饱

笔记 阅读 搞钱 致富 有钱人

亚马逊Lightsail:云服务新篇章,轻松开启您的数字未来

熬夜磕代码、

亚马逊云 AWS Lightsail

SQL INSERT INTO 语句详解:插入新记录、多行插入和自增字段

小万哥

MySQL 数据库 sql 程序员 后端开发

基于FX构建大型Golang应用

俞凡

golang 架构

从 C 迁移到 Rust 的挑战与经验教训_编程语言_InfoQ精选文章