燃爆上海 5·23-24,AICon 大模型实战风暴,50+ 干货一网打尽,即将开幕~ 了解详情
写点什么

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

评论

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

简述JVM垃圾回收

叶鹏

常用设计模式

叶鹏

关于Java调用类的main方法

谷鱼

Java 包位置

一个草根的日常杂碎(9月21日)

刘新吾

生活 现实纪录 随笔

anyRTC云端录制功能上线

anyRTC开发者

WebRTC 语音 直播 RTC 安卓

【性能优化】面试官:Java中的对象都是在堆上分配的吗?

冰河

面试 性能优化 JVM 性能调优 逃逸分析

实战中学习浏览器工作原理 — 排版与渲染

三钻

CSS 大前端 浏览器

架构师第1课作业及学习总结

小诗

Spring 5 中文解析数据存储篇-编程式事物管理

青年IT男

Spring5

前端如何优雅处理类数组对象?

Geek_z9ygea

Java 大前端

小白理财先转变思维理念

boshi

理财 收入 财富自由

高难度对话读书笔记—情绪篇

wo是一棵草

18 张图,一文了解 8 种常见的数据结构

沉默王二

Java 数据结构

微服务的框架(Dubbo)架构

叶鹏

用户密码验证函数

叶鹏

Springboot 定时任务

hepingfly

定时任务 springboot 注解

年度开源盛会 ApacheCon 来临,Apache Pulsar 专场大咖齐聚

Apache Pulsar

开源 云原生 Apache Pulsar 消息中间件

架构师训练营12周作业

叶鹏

食堂卡就餐卡系统

叶鹏

架构师训练营第四周作业

叶鹏

被我玩坏的git:除了之前的工作、当网盘用,还能这么玩

小Q

Java git 程序员 架构 开发

oeasy 教您玩转linux 010304 图形界面 xfce

o

ECMAScript 6新特性简介

程序那些事

nodejs ES6 ECMAScript 6

从零开始搭建完整的电影全栈系统(五)——WEB网站、Api以及爬虫的部署

刘强西

爬虫 网站搭建 部署与维护

两天,我把分布式事务搞完了

yes

分布式事务 seata

Python 中 \x00 和空字符串的区别,以及在 Django 中的坑

AlwaysBeta

Python django 编程

Spring 5 中文解析数据存储篇-@Transactional使用

青年IT男

spring

架构师训练营第八周作业

叶鹏

架构师训练营第7周作业

叶鹏

简述 CAP 原理

叶鹏

一文学懂递归和动态规划!

小齐本齐

算法 数据结构和算法

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