写点什么

从 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:106123

评论

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

单机12万QPS——FunTester复仇记

FunTester

性能测试 接口测试 测试框架 压力测试 测试开发

模块3.架构设计

Geek_ywh40v

什么是JVM?深入解析JVM原理!

愚者

Java JVM

双因子认证是什么意思?有什么作用?

行云管家

信息安全 双因子认证

fil是怎么挖的?Fil矿机有什么配置要求?

区块链 IPFS fil fil矿机 fil矿机配置

详解什么是JMM!

愚者

JMM

洗清杂念 辟除妄见 归于自然|靠谱点评

无量靠谱

体验设计也可以很简单。只要释放出一些善意就可以了。

石云升

用户体验 7月日更 体验设计

博睿数据把脉“体验经济”,以“上帝”视角解决业务运营挑战

博睿数据

智能获客黑科技系统开发搭建

拼多多 +蚂蚁金服 +头条(已拿offer),面试真题分享!

Java 程序员 架构 面试 计算机

Spring Boot基础学习

偏执

面试 后端 spring Boot Starter

现在双非本科的学历还有机会拿到大厂 offer 吗?

java小李

面试 spring Boot Starter

快手技术大咖分享“领域数据建设”实践经验

Geek老T

大数据 数据治理

博睿作为AIOps代表厂商入选《2021年中国ICT技术成熟度曲线报告》

博睿数据

数据链DNA:可观测助力云原生时代服务可达

博睿数据

博睿数据携数据链DNA理念亮相山东 助力金融行业数字化转型

博睿数据

模块三作业

Geek_35a345

底层即真理!Netty+Redis+ZooKeeper解读高并发系统架构!

Java架构追梦

Java redis zookeeper 架构 架构编程

二本的他是如何在12天面试突击中,拿到阿里P7的offer

Java架构师迁哥

有了 NGINX 和 Kong,为什么还需要 Apache APISIX?

API7.ai 技术团队

网关 kong ngnix APISIX

三分钟评估 你的CMDB是“磐石”还是“豆腐渣”

鹿小U

DevOps 运维自动化 CMDB IT运维

百度智能云在视频云解决方案市场位居前三!

百度大脑

云计算 云服务 IDC

Java版人脸检测详解下篇:开发java应用并做成docker镜像同步

编程菌

Java 编程 程序员 技术 技术栈

从零开始学习3D可视化之项目部署

ThingJS数字孪生引擎

大前端 数据 可视化 数字孪生

抖音快手获客系统开发公司

为什么在线自习室这么受年轻人的追捧?

anyRTC开发者

音视频 WebRTC 在线教育 在线自习室 实时直播

ONES Performance 研发效能管理解决方案

万事ONES

研发效能 解决方案 ONES

springboot自动装配源码解析

偏执

面试 后端 spring Boot Starter

用了七个步骤,4面通过拿offer,终“跳进”字节跳动

Java 编程 程序员 架构 面试

持续演进的云原生应用交付

CODING DevOps

DevOps 云原生 k8s 研发工具 交付工具

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