最近在调试一款网络设备,此设备有多路网络连接(大于6路并发),定期1s记录各路带宽的使用量,每60s发送完整的记录给服务器,用以分析时间<>带宽<>流量之间的关联,可以记录网络状态,用户使用量,使用习惯等。每秒1kbytes,60s大约60kbytes。在上传前使用zip打包,发送给java服务后,由后端java服务解压、保存。由爬虫定期做数据挖掘和分析。
前端设备最初为Intel 6550u,i7的性能就是强,所有的操作几乎不占CPU,对用户的带宽也几乎没有影响。后期前端转为Arm cortex-A53 1GHz的时候,问题就来了, zip打包的速度导致上传线程长期阻塞,使用的CPU过高,导致内部DSP处理的数据无法传递给CPU引起DSP error。那么如何能快速的压缩和解压呢?笔者尝试使用了另一种面向字节无损数据压缩算法LZ4。
下面我先简单介绍一下什么是LZ4
LZ4并非新的算法,2011年第一个版本就已经诞生。是一个基于LZ77的面向字节无损数据压缩算法,着重于压缩和解压缩速度。
Linux内核从3.11起,原生实现了LZ4。近期(2019/09/10),Ubuntu的内核团队更是决定从Ubuntu 19.10开始切换到LZ4内核映像压缩,以加快启动时间(之前通常映像是gzip在最后打包)。
在压缩大小方面,GZIP生成最小的压缩内核大小,其次是LZO(比它大16%)和LZ4(比它大25%)。在解压时间上,LZ4比GZIP快7倍以上,LZO比GZIP在x86上快1.25倍左右。即使使用慢速旋转媒体和慢速CPU, LZ4内核的较长的加载时间也会被更快的解压时间所克服。随着媒体速度的加快,GZIP、LZ4和LZO之间的加载时间差减小,解压时间成为主要的速度因素,而LZ4显然是赢家。
LZ4从最初使用C语言制成的参考实现,到现在已经有多种语言的移植和绑定,包括Java、Javacript、Perl、C#、Pytho、Delphi、Go、Rust、Lua等。
在一份评测报告中,LZ4的编码速度达到了memcpy的1/20,解码速度达到惊人的1/3,也就是三个字节的复制运算量就可以还原1字节的内容,是其他编码算法的5倍以上,可见解码效率之高。
Compressor
Ratio
Compression
Decompression
memcpy
1.000
13700 MB/s
13700 MB/s
LZ4 default (v1.9.0)
2.101
780 MB/s
4970 MB/s
LZO 2.09
2.108
670 MB/s
860 MB/s
QuickLZ 1.5.0
2.238
575 MB/s
780 MB/s
Snappy 1.1.4
2.091
565 MB/s
1950 MB/s
2.883
515 MB/s
1380 MB/s
LZF v3.6
2.073
415 MB/s
910 MB/s
2.730
100 MB/s
415 MB/s
LZ4 HC -9 (v1.9.0)
2.721
41 MB/s
4900 MB/s
3.099
36 MB/s
445 MB/s
在另一份测试报告里,综合了多种压缩编码格式的横向比较,
文件压缩尺寸(1到9是传递进的压缩率参数,文件原始尺寸466083840 (445M))
gzip
bzip2
lzma
lzma -e
xz
xz -e
lz4
lzop
1
124875819 (120M)
93997047 (90M)
85618192 (82M)
72042179 (69M)
85630688 (82M)
72069084 (69M)
165844264 (159M)
168012430 (161M)
2
119040249 (114M)
87741348 (84M)
81480824 (78M)
70264395 (68M)
81492504 (78M)
70282944 (68M)
165844264 (159M)
166987891 (160M)
3
114931686 (110M)
84816957 (81MB)
79575087 (76MB)
69015118 (66MB)
79586568 (76MB)
69029204 (66MB)
165844264 (159M)
166987891 (160M)
5
102328357 (98M)
81837328 (79M)
69557610 (67M)
67879362 (65M)
69583428 (67M)
67875988 (65M)
-
166987891 (160M)
7
100128597 (96M)
80197758 (77M)
67276420 (65M)
66868212 (64M)
67294092 (65M)
66852780 (64M)
-
116205578 (111M)
9
99740486 (96M)
78963640 (76M)
65841213 (63M)
65362226 (63M)
65859432 (63M)
65372696 (63M)
-
114824102 (110M)
压缩比
gzip
bzip2
lzma
lzma -e
xz
xz -e
lz4
lzop
1
26.8%
20.2%
18.4%
15.5%
18.4%
15.5%
35.6%
36.0%
2
25.5%
18.8%
17.5%
15.1%
17.5%
15.1%
35.6%
35.8%
3
24.7%
18.2%
17.1%
14.8%
17.1%
14.8%
35.6%
35.8%
5
22.0%
17.6%
14.9%
14.6%
14.9%
14.6%
-
35.8%
7
21.5%
17.2%
14.4%
14.3%
14.4%
14.3%
-
24.9%
9
21.4%
16.9%
14.1%
14.0%
14.1%
14.0%
-
24.6%
压缩时间
gzip
bzip2
lzma
lzma -e
xz
xz -e
lz4
lzop
1
8.1s
58.3s
31.7s
4m37s
32.2s
4m40s
1.3s
1.6s
2
8.5s
58.4s
40.7s
4m49s
41.9s
4m53s
1.4s
1.6s
3
9.6s
59.1s
1m2s
4m36s
1m1s
4m39s
1.3s
1.5s
5
14s
1m1s
3m5s
5m
3m6s
4m53s
-
1.5s
7
21s
1m2s
4m14s
5m52s
4m13s
5m57s
-
35s
9
33s
1m3s
4m48s
6m40s
4m51s
6m40s
-
1m5s
解压时间
gzip
bzip2
lzma
lzma -e
xz
xz -e
lz4
lzop
1
3.5s
3.4s
6.7s
5.9s
7.2s
6.5s
0.4s
1.5s
2
3s
15.7
6.3s
5.6s
6.8s
6.3s
0.3s
1.4s
3
3.2s
15.9s
6s
5.6s
6.7s
6.2s
0.4s
1.4s
5
3.2s
16s
5.5s
5.4s
6.2s
6s
-
1.5s
7
3s
15s
5.3s
5.3s
5.9s
5.8s
-
1.3s
9
3s
15s
5s
5.1s
5.6s
5.6s
-
1.2s
编码时内存使用量
gzip
bzip2
lzma
lzma -e
xz
xz -e
lz4
lzop
1
0.4MB
1.1MB
8.3MB
12.6MB
8.3MB
12.5MB
12MB
0.7MB
2
0.4MB
1.9MB
15.8MB
24MB
15.8MB
24MB
12MB
0.7MB
3
0.4MB
2.7MB
30.7MB
46.9MB
30.8MB
47M
13.2MB
0.7MB
5
0.4MB
4.2MB
93MB
93MB
93MB
93MB
-
0.7MB
7
0.4MB
5.7MB
185MB
185MB
185MB
185MB
-
0.9MB
9
0.4MB
7.2MB
672MB
673MB
673MB
673MB
-
0.9MB
解码时内存使用量
gzip
bzip2
lzma
lzma -e
xz
xz -e
lz4
lzop
1
0.1MB
0.3MB
1MB
1MB
1MB
1MB
13MB
0.2MB
2
0.1MB
0.9MB
2.1MB
2.1MB
2.1MB
2.1MB
12MB
0.2MB
3
0.1MB
1.1MB
4.1MB
4.1MB
4.0MB
4.0MB
13MB
0.2MB
5
0.1MB
1.9MB
8.1MB
8.1MB
8.1MB
8.1MB
-
0.2MB
7
0.1MB
2.6MB
16MB
16MB
16MB
16MB
-
0.2MB
9
0.1MB
3.4MB
64MB
64MB
64MB
64MB
-
0.2MB
奇怪的是内存使用量奇高,将在以后的文章中分析。
根据这些评估,可以看出来,LZ4虽然压缩比不高,但是编码和解码速度奇快。在应用场景上,更多的在需要压缩、解压,并且对速度有很高要求的情况。
使用LZ4带来的好处
转换编码为LZ4后,cpu资源占用降低,速度变快,虽然带宽占用增加,但也有限,并且对原先代码结构变更量极低,不需要调整线程优先级,不需要调整系统结构,并且可以统一x86与arm代码库。目前设备运行正常。
LZ4在低功耗,cpu算力低的设备中特别适用,也是linux映像选择的原因,在内容中加载小文件解码的过程比通过emmc、nand、nor flash等直接加载镜像的速度会快的多。