如何不重写代码就将数据压缩速度提升四倍
为什么要改动一个从九十年代就稳定运行的东西
最初的 zlib 库诞生于三十年前。Mark Adler 和 Jean-Loup Gailly 创建了一个极其可靠且可移植的工具。它几乎无处不在,从智能手表到老旧操作系统都能运行。但这种通用性是有代价的。
为了兼容旧编译器和 16 位环境,zlib 代码不得不携带大量兼容性处理。代码中充斥着内存限制检查和支持老旧处理器的宏定义。正因如此,现代开发者针对向量指令的新优化根本无法合并到主仓库。Mark Adler 对稳定性要求极高,这种做法完全可以理解。
就在这时,Hans Christian Rosbach (Dead2) 决定将社区积累的补丁整合到一个分支中。zlib-ng 项目就这样诞生了。
zlib-ng 内部发生了什么变化
这个分支的理念很简单:抛弃古老的遗留代码,应用现代 CPU 的向量指令。开发者整合了来自 Intel 和 Cloudflare 的补丁,清理了九十年代的示例,并用 C11 重写了关键算法。
效果是显著的。在 x86-64 架构上,压缩和解压速度比标准 zlib 快约四倍。
性能提升的驱动因素:
- 针对不同架构的向量指令。x86 平台使用 AVX2、AVX-512、SSSE3,ARM 使用 NEON,以及 POWER、RISC-V、LoongArch 和 IBM Z 的向量单元。
- 运行时自动检测 CPU 能力。编译后的二进制文件本身就知道在特定处理器上调用哪些向量化函数。
- 快速 deflate 算法。采用了 Intel 在优化重复搜索、哈希移位和 CRC32 计算方面的工作成果。
- 安全的非对齐访问。非对齐内存的读写得到优化,位缓冲区也进行了更新。
同时,该项目并不试图打破现有世界。该库可以构建为与标准 zlib 完全兼容的 API,也可以使用其自身更新的原生 API。
如何构建和亲自体验
该项目有两种构建系统:CMake 和传统的 configure。
如果使用 CMake,构建过程很熟悉:
参数 启用与经典 zlib 的完全兼容模式。输出是一个可以替代系统 的库。
对于熟悉 的粉丝来说,构建过程没有区别:
如果使用 vcpkg 依赖管理器,根本不需要手动拉取源码:
使用 LD_PRELOAD 加速应用程序
zlib-ng 最有趣的技巧之一是,无需更改代码就能加速现有软件。如果你的程序动态链接了 ,可以在运行时替换该库。
启动临时替换的实用工具方法如下:
应用程序立即利用处理器的向量指令,无需对二进制文件进行任何更改。
但是,分支的作者在 README 中特别警告:不要试图在 Linux 目录的发行版级别替换系统 。如果出现问题或出现罕见的兼容性问题,整个系统将崩溃,包括系统服务。更安全的方法是安装到单独的目录(如 )并显式链接。
代码测试覆盖率如何
负责数据压缩的代码必须无故障运行。即使丢失一位也会使存档变成垃圾。zlib-ng 作者认真对待测试。
仓库采用全面的检查套件:
- 通过 OSS-Fuzz 进行内存消毒剂和模糊测试。
- 通过 QEMU 对 ARM、PowerPC、RISC-V、SPARC64 和 S390x 进行原生和仿真 CI。
- 基于 Google Test 的单元测试。
- 使用 Google Benchmark 进行性能测量。
通过持续模糊测试,该库保持了高水平的可靠性,这在替换核心系统组件时至关重要。
谁将从 zlib-ng 中受益
首先,对于数据压缩和解压成为瓶颈的团队来说,这个分支非常有用。如果你正在处理 GB 级的日志、使用 nginx 等 Web 服务器、在游戏开发中压缩纹理,或在后端处理海量数据,zlib-ng 将带来显著的速度提升。
另一方面,如果你的软件运行在古老的 16 位微控制器或完全小众的二三十年前的操作系统上,就没有必要动这个久经考验的原始 zlib。这个分支是专门为现代平台创建的。
这个工具看起来很成熟,并且由社区积极维护。如果你需要在现代硬件上获得最大的 DEFLATE 和 gzip 性能,值得花几个小时对你的数据进行基准测试。
相关项目