10 个版本

使用旧的 Rust 2015

0.4.1 2024 年 4 月 26 日
0.3.5 2021 年 4 月 22 日
0.3.4 2020 年 1 月 29 日
0.3.3 2019 年 11 月 25 日
0.1.1 2019 年 8 月 15 日

#162图像

Download history 186/week @ 2024-05-01 33/week @ 2024-05-08 48/week @ 2024-05-15 77/week @ 2024-05-22 49/week @ 2024-05-29 34/week @ 2024-06-05 28/week @ 2024-06-12 43/week @ 2024-06-19 77/week @ 2024-06-26 77/week @ 2024-07-03 25/week @ 2024-07-10 42/week @ 2024-07-17 42/week @ 2024-07-24 31/week @ 2024-07-31 45/week @ 2024-08-07 38/week @ 2024-08-14

170 每月下载量
用于 2 crate

MIT 许可证

7.5MB
2.5K SLoC

Rust 2.5K SLoC // 0.2% comments Swift 146 SLoC // 0.2% comments Batch 113 SLoC C 88 SLoC // 0.3% comments Shell 42 SLoC // 0.2% comments

mtpng

Rust 中的并行化 PNG 编码器

由 Brooke Vibber [email protected]

背景

在大型图像尺寸下压缩 PNG 文件是一种相对较慢的操作,4K 分辨率及以上可能需要从半秒到一秒以上。有关更多详细信息,请参阅 我的关于该主题的博客系列

传统 libpng 中最大的 CPU 成本似乎是过滤,这很容易并行化,以及 deflate 压缩,这可以在块边界之间稍微损失压缩的情况下并行处理。

pigz 是一个著名的并行化 deflate/gzip 压缩的 C 实现,也是这里使用的分块方案的强大灵感来源。

我还受到了 Pascal Beyeler 的一个名为 png-parallel 的实验性 C++/OpenMP 项目的启发,该项目没有实现过滤,但证实了基本理论。

状态

在所有颜色格式中创建正确的文件(输入必须预先打包)。在大文件上表现良好,但需要在小文件和辅助块上做更多工作。计划很快稳定 API,但尚未完成——在 1.0 之前,事情将会发生变化。

目标

性能

  • ☑️ 一定要在多线程中比 libpng 快
  • ☑️ 应该与单线程的 libpng 一样快或更快

功能

  • ☑️ 一定要支持所有标准颜色类型和深度
  • ☑️ 一定要支持所有标准过滤模式
  • ☑️ 一定要压缩得与 libpng 几乎一样好
  • 可能比 libpng 实现更好的压缩,但不能以牺牲性能为代价
  • ☑️ 应该支持流式输出
  • 可能支持交错

兼容性

  • ☑️ 一定要有一个好的 Rust API(进行中)
  • ☑️ 一定要有一个好的 C API(进行中)
  • ☑️ 一定要在 Linux x86、x86_64 上运行
  • ☑️ 一定要在 Linux arm、arm64 上运行
  • ☑️ 应该在 macOS x86_64 上运行
  • ☑️ 应该在 iOS arm64 上运行
  • ☑️ 应该在 Windows x86、x86_64 上运行
  • ☑️️ 应该在 Windows arm64 上运行

压缩

压缩率比使用双-4K截图和结构照片的libpng略低,当前默认的256 KiB块大小,随着其增大而逐渐接近。

使用较小的块大小或启用流模式,可以在略微增加文件大小的同时,获得更高的并行性(小块)和更低的延迟(流式传输)。

在0.3.5版本中,对过滤器启发式算法进行了修正,以在某些与libpng不同的情境中匹配libpng;现在作为替换使用时,应提供与libpng非常相似的结果。后续的研究可能涉及更改启发式算法,因为它无法正确预测许多截图风格真彩色图像的“无”过滤器的良好性能。

性能

请注意,未优化的调试构建比优化后的发布构建慢约50倍。请始终使用--release运行!

截至2018年9月26日,使用Rust 1.29.0,在Linux x86_64上的单线程性能比libpng快约30-40%,保存相同双-4K截图样本图像。使用多线程可以持续击败libpng,并且至少在8个物理核心上具有良好的扩展性。

有关各种设备的非正式基准测试,请参阅docs/perf.md

在默认设置下,未压缩数据小于128 KiB的文件不会看到多线程带来的任何好处,但可能仍然比libpng运行得更快,因为过滤速度更快。

待办事项

有关详细信息,请参阅GitHub上的项目列表

构建说明

使用Cargo构建过程;请注意,libz_sys被拉入,它可能在某些未提供标准库的平台(如Windows)上构建zlib C库。

有两个用户可见的功能标志

  • capi构建并导出兼容C的API符号;仅在您计划将生成的库与调用它的C/C++代码链接时需要
  • cli构建用于测试/演示的命令行工具以及库

在纯Rust程序中使用mtpng或在混合C-Rust程序中仅使用Rust部分时,不需要使用任一标志。

用法

注意:Rust和C API尚不稳定,将在1.0之前发生变化。

Rust用法

有关详细信息,请参阅crate API文档

可以mtpng CLI工具作为编写文件的示例。

简而言之,类似于以下内容

let mut writer = Vec::<u8>::new();

let mut header = Header::new();
header.set_size(640, 480)?;
header.set_color(ColorType::TruecolorAlpha, 8)?;

let mut options = Options::new();

let mut encoder = Encoder::new(writer, &options);

encoder.write_header(&header)?;
encoder.write_image_rows(&data)?;
encoder.finish()?;

C用法

有关C头文件的详细信息,请参阅c/mtpng.h,该头文件连接到mtpng::capi模块中的不安全Rust包装函数。

在Linux或macOS上构建C示例,请运行make。在Windows上,运行build-win.bat x64进行x86-64原生构建,或者传递x86arm64以构建这些平台。

这将从sample.c构建一个sample可执行文件,以及一个libmtpng.solibmtpng.dylibmtpng.dll库,以便于链接。它会在out/csample.png中产生输出文件。

数据流

编码可以分解为多个并行块

Encoder data flow diagram

解码不能;它必须以流的形式运行,但可以实现管道(尚未实现)

Decoder data flow diagram

依赖项

Rayon 用于其 ThreadPool 实现。您可以使用默认的 Rayon 全局池或自定义 ThreadPool 实例创建编码器。

crc 用于计算 PNG 块校验和。

libz-sys 用于封装 libz 以进行 deflate 压缩。我简要地看了看纯 Rust 实现,但找不到任何支持原始流输出、字典设置以及在不关闭流的情况下刷新到字节边界的实现。

itertools 用于在过滤器中管理迭代。

png 由 CLI 工具用于加载输入文件以进行测试重新压缩。

clap 由 CLI 工具用于处理选项解析和帮助显示。

time 由 CLI 工具用于计时压缩。

许可证

您可以在以下 MIT 风格许可证下使用此软件

版权 (c) 2018-2024 Brooke Vibber

特此授予任何获得此软件及其相关文档副本(以下简称“软件”)的人免费使用软件的权利,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售软件副本,以及允许向提供软件的人做上述行为,前提是遵守以下条件

上述版权声明和本许可声明应包含在软件的所有副本或主要部分中。

软件按“原样”提供,不提供任何形式的保证,无论是明示的还是暗示的,包括但不限于适销性、特定用途适用性和非侵权性保证。在任何情况下,作者或版权所有者均不对任何索赔、损害或其他责任承担责任,无论这些责任源于合同、侵权或其他原因,是否与软件或软件的使用或其他交易有关。

依赖项

~2.3–3.5MB
~65K SLoC