Skip to content

Day 88: std.zstd模块:Zstandard现代压缩

Zstandard(简称Zstd)是由Facebook的Yann Collet(也是LZ4的作者)开发的现代无损压缩算法。它的设计目标是提供与传统Zlib/Deflate算法相当的压缩率,但压缩和解压速度要快得多,接近LZ4的水平。

由于其出色的综合性能,Zstd正迅速成为许多应用场景(如数据库、文件系统、包管理和实时数据传输)中的首选压缩算法。Zig标准库通过std.zstd模块,为Zstd提供了完整的原生支持。

与标准库中的其他压缩模块一样,std.zstd提供了易于使用的流式API。

  • zstd.compressStream(writer, options): 创建一个Zstd压缩流。
  • zstd.decompressStream(reader, options): 创建一个Zstd解压缩流。

这些API允许你以块的方式处理数据,非常适合大文件和网络流。

Zstd最显著的特点之一是它提供了非常宽泛的压缩级别范围。级别越低,速度越快,但压缩率也越低。级别越高,压缩率越高,但会消耗更多的CPU和内存。

  • 正数级别 (1-22): 级别越高,压缩率越高。级别3通常被认为是速度和压缩率之间的一个良好平衡点。
  • 负数级别: Zstd还提供了负数级别,它们是“超快速”模式,速度比LZ4还快,但压缩率较低。

你可以在compressStream或compress的Options中设置level字段。

const options = .{ .level = 9 }; // 设置一个较高的压缩级别
const compressed_data = try zstd.compress(allocator, data, options);

下面是一个使用流式API来压缩文件的例子,这对于无法一次性读入内存的大文件至关重要。

const std = @import("std");
const zstd = std.zstd;
pub fn main() !void {
var in_file = try std.fs.cwd().openFile("large_data.bin", .{});
defer in_file.close();
var out_file = try std.fs.cwd().createFile("large_data.bin.zst", .{});
defer out_file.close();
var compressor = zstd.compressStream(out_file.writer(), .{ .level = 3 });
// std.io.copy 会自动处理缓冲和流式读写
try std.io.copy(in_file.reader(), compressor.writer());
// 结束流以确保所有数据都被刷新
try compressor.end();
}

Zstd的字典压缩功能非常强大。当你有大量相似的小文件(例如,很多小的JSON消息)时,你可以从这些样本数据中“训练”出一个字典。然后,在压缩每个小文件时使用这个字典,可以极大地提高压缩率。

std.zstd.dict模块提供了train函数。尝试:

  1. 创建一组相似的字符串样本。
  2. 使用zstd.dict.train从这些样本中生成一个字典。
  3. 使用这个字典来压缩其中一个样本,并与不使用字典的压缩大小进行比较。
  • 多线程压缩 Zstd原生支持使用多个线程来并行压缩单个文件,以加快速度。std.zstd的Options中有一个nb_workers字段,你可以设置它来启用多线程压缩。

  • 帧头和校验和 Zstd文件(.zst)有标准的帧格式,包含了魔数、帧头和可选的内容校验和。std.zstd模块会自动处理这些帧的创建和解析,确保了与其他Zstd工具的兼容性。

  • 解压缩时需要多少内存? Zstd的一个优点是,解压缩所需的内存由压缩时使用的窗口大小决定,并且这个信息被编码在帧头中。这意味着解压缩器可以在开始解压之前就知道需要分配多少内存,从而避免了意外的内存耗尽。

Zstandard可以说是集LZ4的速度和Zlib的压缩率于一身的现代压缩算法。它在几乎所有方面都提供了卓越的性能和灵活性。

Zig标准库将Zstd作为一等公民,提供了完整的原生实现,这再次凸显了Zig对现代、高性能计算的承诺。对于绝大多数新的应用程序,如果需要在压缩率和速度之间做出选择,Zstandard通常都是正确答案。它正在迅速取代Zlib和Gzip,成为互联网基础设施和数据驱动型应用的新标准。