写点什么

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

评论

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

听说你对explain 很懂?

艾小仙

sql 大前端

十大排序算法--桶排序

Ayue、

排序算法 8月日更

工作多年,分享16条职场经验给新人朋友

架构精进之路

职场 成长 经验分享 8月日更

iOS开发:上架遇到NSBluetoothAlwaysUsageDescription被拒问题(已解决)

三掌柜

8月日更 8月

从0开始的TypeScriptの二:类型系统

空城机

JavaScript typescript 8月日更

The Data Way Vol.1|风口下的开源市场:如何看待开源与商业的关系?

SphereEx

数据库 开源

架构实战营-毕业总结

阿体

kubernetes入门:dashboard可视化界面部署springboot项目

小鲍侃java

8月日更

万字长文讲透低代码

百度开发者中心

最佳实践 开发者 方法论 低代码 语言 & 开发

架构实战营 毕业设计

Dylan

架构实战营

Docker新手FAQ系列 1

混沌畅想

容器最佳实践系列-查看docker的文件系统

混沌畅想

【“互联网+”大赛华为云赛道】IoT命题攻略:仅需四步,轻松实现场景智能化设计

华为云开发者联盟

IoT 华为云 LiteOS 互联网+ IoT边缘

【“互联网+”大赛华为云赛道】EI命题攻略:华为云EI的能力超丰富,助你实现AI梦想

华为云开发者联盟

大数据 modelarts 大赛 互联网+ 华为云EI

使用Grafana显示Prometheu监控

Rubble

Grafana Prometheus 8月日更

netty系列之:中国加油

程序那些事

Java Netty nio 程序那些事

设计电商秒杀系统

thewangzl

毕业设计-电商秒杀系统

chenmin

字节跳动《实时音视频通讯技术》学习笔记之RTC概述及技术简介

Regan Yue

音视频 RTC 8月日更

手撸二叉树之二叉搜索树中的搜索

HelloWorld杰少

数据结构与算法 8月日更

vivo 全球商城:优惠券系统架构设计与实践

vivo互联网技术

服务器 架构设计

AI + K8S 驱动存储技术变革

焱融科技

人工智能 Kubernetes 云原生 高性能 存储

带你看论文丨全局信息对于图网络文档解析的影响

华为云开发者联盟

文档 CNN网络 图网络 非结构化文档 全局信息

从技术到文案,还回技术么?

escray

学习 极客时间 朱赟的技术管理课 8月日更

【Flutter 专题】73 图解自定义 ACECheckBox 复选框

阿策小和尚

Flutter 小菜 0 基础学习 Flutter Android 小菜鸟 8月日更

oeasy教您玩转vim - 11 - # 向前向后

o

十万高层齐卸甲,竟无一人有慈心。前阿里员工看阿里高管不法侵害女员工事件

刘悦的技术博客

阿里巴巴 面试 职场 职场 PUA

浅谈BU安全建设

I

项目管理 企业安全 BU安全 安全建设

简单的Postman,还能玩出花?

码农参上

8月日更

iPhone Shortcuts 使用与场景

TroyLiu

iphone 效率工具 快捷指令 shortcuts nfc

TCP如何进行拥塞控制

W🌥

计算机网络 TCP/IP 8月日更

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