Zstd 压缩性能分析
About Zstd
Zstd,全称 Zstandard,是 Facebook 于 2016 年开源的新无损压缩算法。与 zlib、lz4、xz 等当前流行的压缩算法不同,Zstd 寻求一种压缩性能与压缩率通吃的方案,而实际上它也确实做到了。在由官方所列出的表格中,可以看到,Zstd 不仅具备优秀的压缩性能,在压缩率上也有非常亮眼的表现。
综合来说,Zstd 具有以下特性:
- 在压缩性能和压缩率上均有很突出的表现
- 支持以训练方式生成字典文件,可显著提高对小数据包的压缩率
About Zstd-jni
Zstd-jni,顾名思义,是基于 Zstd 本地库实现的 Java 调用接口。它支持通过 Java 语言实现 Zstd 的压缩与解压缩。
在 Zstd-jni 的三方包中,主要实现了以下功能:
- 提供静态的压缩与解压缩方法
- 支持压缩数据的流式传输
- 支持字典文件的训练与添加
Code Example
下面示例代码将展示 Zstd-jni 的压缩、解压、字典训练等功能:
package com.panda.zstd;
import com.github.luben.zstd.Zstd;
import com.github.luben.zstd.ZstdDictCompress;
import com.github.luben.zstd.ZstdDictDecompress;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* zstd 示例
*
* @author panda
* @date 2020/7/29
*/
public class ZstdDemo {
private static final Logger LOGGER = LoggerFactory.getLogger(ZstdDemo.class);
// 训练文件
private static final String TRAIN_FILE = "C:\\Users\\admin\\Desktop\\train\\train-10000.txt";
// 待压缩文件
private static final String COMPRESS_FILE = "C:\\Users\\admin\\Desktop\\train\\test.txt";
// 压缩等级,以时间换空间
private static final Integer LEVEL = 10;
// 解压时字节数组的最大值
private static final Integer MAX_SIZE = 1000000;
private static ZstdDictCompress compressDict;
private static ZstdDictDecompress decompressDict;
public static void main(String[] args) throws IOException {
String s = FileUtils.readFileToString(new File(COMPRESS_FILE), StandardCharsets.UTF_8);
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
train();
compress(bytes);
}
public static void train() throws IOException {
// 初始化词典对象
String dictContent = FileUtils.readFileToString(new File(TRAIN_FILE), StandardCharsets.UTF_8);
byte[] dictBytes = dictContent.getBytes(StandardCharsets.UTF_8);
compressDict = new ZstdDictCompress(dictBytes, LEVEL);
decompressDict = new ZstdDictDecompress(dictBytes);
}
public static void compress(byte[] bytes) {
LOGGER.info("raw data length: {}", bytes.length);
// 压缩数据
long compressBeginTime = System.currentTimeMillis();
byte[] compressed = Zstd.compress(bytes, compressDict);
long compressEndTime = System.currentTimeMillis();
LOGGER.info("compress spend time: {}", compressEndTime - compressBeginTime);
LOGGER.info("compressed data length: {}", compressed.length);
// 解压数据
long decompressBeginTime = System.currentTimeMillis();
// 第 3 个参数不能小于解压后的字节数组的大小
byte[] decompressed = Zstd.decompress(compressed, decompressDict, MAX_SIZE);
long decompressEndTime = System.currentTimeMillis();
LOGGER.info("decompress spend time: {}", decompressEndTime - decompressBeginTime);
LOGGER.info("decompressed data length: {}", decompressed.length);
}
}
Performance Testing
当前,我们具有以下需求场景:使用 Flume 收集与发送日志,在接收到日志后,对日志对象的 body 进行压缩,再传递给下游,以减少网络传输的开销。据此可以了解到,实际上我们是希望通过 Zstd 实现小文件的压缩与解压。
在上面我们提到过,Zstd 支持以训练方式生成字典文件,可提高对小数据包的压缩率。显然,这个特性非常适合我们的需求场景。因此,我们决定将字典作为自变量,测试在不同样本集的字典下,Zstd 的压缩性能与压缩率。
通过测验,我们希望能解决以下疑问:
- Zstd 是否能满足小文件的压缩与解压需求
- Zstd 的压缩性能如何,是否会造成数据延时
- 字典的引入能否提高压缩率,如何选择正确的样本集
首先,我们准备如下 4 份字典文件:
- train-A:表示含有 5000 条样本日志的字典文件
- train-B:表示含有 10000 条样本日志的字典文件
- train-C:表示含有 15000 条样本日志的字典文件
- train-D:表示含有 20000 条样本日志的字典文件
然后,我们输入 10000 行日志数据,对这些字典分别进行性能测验,测验的指标包含压缩率、压缩时间与解压时间。
测验后,对各指标取平均值,计算结果如下表所示:
dict | sample num | compress ratio(%) | compress time(ms) | decompress time(ms) |
---|---|---|---|---|
none | 0 | 54.08 | 0.03 | 0.10 |
train-A | 5000 | 81.85 | 0.15 | 0.10 |
train-B | 10000 | 82.20 | 0.15 | 0.09 |
train-C | 15000 | 82.06 | 0.15 | 0.09 |
train-D | 20000 | 81.07 | 0.15 | 0.08 |
注:ratio = 文件原大小 - 压缩后的大小 / 文件原大小。
结合测验过程及测验结果,我们得出以下结论:
- zstd 可以满足小文件的压缩与解压需求,经压缩-解压后,数据保持无损状态
- zstd 的压缩性能非常优秀,平均压缩与解压消耗时间甚至未达到毫秒级别,不会造成数据延时
- 字典引入可以显著提高压缩率,但会在一定程度上影响压缩性能
- 字典样本集并非越大越好,合适的样本集更能提高压缩率,根据测验结果,建议样本集数量维持在 10000 左右
Reference
- https://facebook.github.io/zstd/
- https://engineering.fb.com/core-data/smaller-and-faster-data-compression-with-zstandard/
- https://github.com/facebook/zstd
- https://github.com/luben/zstd-jni
本文地址:https://blog.csdn.net/magicpenta/article/details/107689459
上一篇: Java之Map接口和泛型