Java 不同压缩算法的性能比较

转载自:https://mp.weixin.qq.com/s/df1y0KbMShZveTKNBJ3aPA

本文将会对常用的几个压缩算法的性能作一下比较。进行比较的算有:

  • JDK GZIP ——这是一个压缩比高的慢速算法,压缩后的数据适合长期使用。JDK中的java.util.zip.GZIPInputStream / GZIPOutputStream便是这个算法的实现。
  • JDK deflate ——这是JDK中的又一个算法(zip文件用的就是这一算法)。它与gzip的不同之处在于,你可以指定算法的压缩级别,这样你可以在压缩时间和输出文件大小上进行平衡。可选的级别有0(不压缩),以及1(快速压缩)到9(慢速压缩)。它的实现是java.util.zip.DeflaterOutputStream / InflaterInputStream。
  • LZ4压缩算法的Java实现——这是本文介绍的算法中压缩速度最快的一个,与最快速的deflate相比,它的压缩的结果要略微差一点。
  • Snappy——这是Google开发的一个非常流行的压缩算法,它旨在提供速度与压缩比都相对较优的压缩算法。我用来测试的是这个实现。它也是遵循Apache 2.0许可证发布的。

测试文件的大小是354,509,602字节(338MB),测试结果:

输出文件的大小

实现 文件大小(字节)
GZIP 64,200,201
Snappy (normal) 138,250,196
Snappy (framed) 101,470,113
LZ4 (fast) 98,316,501
LZ4 (high) 82,076,909
Deflate (lvl=1) 78,369,711
Deflate (lvl=2) 75,261,711
Deflate (lvl=3) 73,240,781
Deflate (lvl=4) 68,090,059
Deflate (lvl=5) 65,699,810
Deflate (lvl=6) 64,200,191
Deflate (lvl=7) 64,013,638
Deflate (lvl=8) 63,845,758
Deflate (lvl=9) 63,839,200

Java 不同压缩算法的性能比较
可以看出文件的大小相差悬殊(从60Mb到131Mb)。

压缩时间

实现 压缩时间(ms)
Snappy.framedOutput 2264.700
Snappy.normalOutput 2201.120
Lz4.testFastNative 1056.326
Lz4.testFastUnsafe 1346.835
Lz4.testFastSafe 1917.929
Lz4.testHighNative 7489.958
Lz4.testHighUnsafe 10306.973
Lz4.testHighSafe 14413.622
deflate (lvl=1) 4522.644
deflate (lvl=2) 4726.477
deflate (lvl=3) 5081.934
deflate (lvl=4) 6739.450
deflate (lvl=5) 7896.572
deflate (lvl=6) 9783.701
deflate (lvl=7) 10731.761
deflate (lvl=8) 14760.361
deflate (lvl=9) 14878.364
GZIP 10351.887

Java 不同压缩算法的性能比较

吞吐量及效率

实现 时间(ms) 未压缩文件大小(Mb) 吞吐量(Mb/秒) 压缩后文件大小(Mb)
Snappy.normalOutput 2201.12 338 153.5581885586 131.8454742432
Snappy.framedOutput 2264.7 338 149.2471409017 96.7693328857
Lz4.testFastNative 1056.326 338 319.9769768045 93.7557220459
Lz4.testFastSafe 1917.929 338 176.2317583185 93.7557220459
Lz4.testFastUnsafe 1346.835 338 250.9587291688 93.7557220459
Lz4.testHighNative 7489.958 338 45.1270888301 78.2680511475
Lz4.testHighSafe 14413.622 338 23.4500391366 78.2680511475
Lz4.testHighUnsafe 10306.973 338 32.7933332124 78.2680511475
deflate (lvl=1) 4522.644 338 74.7350443679 74.7394561768
deflate (lvl=2) 4726.477 338 71.5120374012 71.7735290527
deflate (lvl=3) 5081.934 338 66.5101120951 69.8471069336
deflate (lvl=4) 6739.45 338 50.1524605124 64.9452209473
deflate (lvl=5) 7896.572 338 42.8033835442 62.6564025879
deflate (lvl=6) 9783.701 338 34.5472536415 61.2258911133
deflate (lvl=7) 10731.761 338 31.4952969974 61.0446929932
deflate (lvl=8) 14760.361 338 22.8991689295 60.8825683594
deflate (lvl=9) 14878.364 338 22.7175514727 60.8730316162
GZIP 10351.887 338 32.651051929 61.2258911133

Java 不同压缩算法的性能比较

  可以看到,其中大多数实现的效率是非常低的:在Xeon E5-2650处理器上,高级别的deflate大约是23Mb/秒,即使是GZIP也就只有33Mb/秒,这大概很难令人满意。同时,最快的defalte算法大概能到75Mb/秒,Snappy是150Mb/秒,而LZ4(快速,JNI实现)能达到难以置信的320Mb/秒!
  从表中可以清晰地看出目前有两种实现比较处于劣势:Snappy要慢于LZ4(快速压缩),并且压缩后的文件要更大。相反,LZ4(高压缩比)要慢于级别1到4的deflate,而输出文件的大小即便和级别1的deflate相比也要大上不少。

  因此如果需要进行“实时压缩”的话,肯定会在LZ4(快速)的JNI实现或者是级别1的deflate中进行选择。当然如果你的公司不允许使用第三方库的话你也只能使用deflate了。你还要综合考虑有多少空闲的CPU资源以及压缩后的数据要存储到哪里。比方说,如果你要将压缩后的数据存储到HDD的话,那么上述100Mb/秒的性能对你而言是毫无帮助的(假设你的文件足够大的话)——HDD的速度会成为瓶颈。同样的文件如果输出到SSD硬盘的话——即便是LZ4在它面前也显得太慢了。如果你是要先压缩数据再发送到网络上的话,最好选择LZ4,因为deflate75Mb/秒的压缩性能跟网络125Mb/秒的吞吐量相比真是小巫见大巫了。

总结
  如果你认为数据压缩非常慢的话,可以考虑下LZ4(快速)实现,它进行文本压缩能达到大约320Mb/秒的速度——这样的压缩速度对大多数应用而言应该都感知不到。
  如果你受限于无法使用第三方库或者只希望有一个稍微好一点的压缩方案的话,可以考虑下使用JDK deflate(lvl=1)进行编解码——同样的文件它的压缩速度能达到75Mb/秒。