`
deepinmind
  • 浏览: 451377 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
1dc14e59-7bdf-33ab-841a-02d087aed982
Java函数式编程
浏览量:41617
社区版块
存档分类
最新评论

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

阅读更多
本文将会对常用的几个压缩算法的性能作一下比较。结果表明,某些算法在极端苛刻的CPU限制下仍能正常工作。


文中进行比较的算有:
  • JDK GZIP ——这是一个压缩比高的慢速算法,压缩后的数据适合长期使用。JDK中的java.util.zip.GZIPInputStream / GZIPOutputStream便是这个算法的实现。
  • JDK deflate ——这是JDK中的又一个算法(zip文件用的就是这一算法)。它与gzip的不同之处在于,你可以指定算法的压缩级别,这样你可以在压缩时间和输出文件大小上进行平衡。可选的级别有0(不压缩),以及1(快速压缩)到9(慢速压缩)。它的实现是java.util.zip.DeflaterOutputStream / InflaterInputStream。
  • [url=http://en.wikipedia.org/wiki/LZ4_%28compression_algorithm%29]
  • LZ4压缩算法[/url]的Java实现——这是本文介绍的算法中压缩速度最快的一个,与最快速的deflate相比,它的压缩的结果要略微差一点。如果想搞清楚它的工作原理,我建议你读一下这篇文章。它是基于友好的Apache 2.0许可证发布的。
  • Snappy——这是Google开发的一个非常流行的压缩算法,它旨在提供速度与压缩比都相对较优的压缩算法。我用来测试的是[这个实现[/url]。它也是遵循Apache 2.0许可证发布的。

  • 压缩测试

    要找出哪些既适合进行数据压缩测试又存在于大多数Java开发人员的电脑中(我可不希望你为了运行这个测试还得个几百兆的文件)的文件也着实费了我不少工夫。最后我想到,大多数人应该都会在本地安装有JDK的文档。因此我决定将javadoc的目录整个合并成一个文件——拼接所有文件。这个通过tar命令可以很容易完成,但并非所有人都是Linux用户,因此我写了个程序来生成这个文件:


    
    
    public class InputGenerator {
        private static final String JAVADOC_PATH = "your_path_to_JDK/docs";
        public static final File FILE_PATH = new File( "your_output_file_path" );
     
        static
        {
            try {
                if ( !FILE_PATH.exists() )
                    makeJavadocFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
     
        private static void makeJavadocFile() throws IOException {
            try( OutputStream os = new BufferedOutputStream( new FileOutputStream( FILE_PATH ), 65536 ) )
            {
                appendDir(os, new File( JAVADOC_PATH ));
            }
            System.out.println( "Javadoc file created" );
        }
     
        private static void appendDir( final OutputStream os, final File root ) throws IOException {
            for ( File f : root.listFiles() )
            {
                if ( f.isDirectory() )
                    appendDir( os, f );
                else
                    Files.copy(f.toPath(), os);
            }
        }
    }
    
    
    


    在我的机器上整个文件的大小是354,509,602字节(338MB)。

    测试

    一开始我想把整个文件读进内存里,然后再进行压缩。不过结果表明这么做的话即便是4G的机器上也很容易把堆内存空间耗尽。

    于是我决定使用操作系统的文件缓存。这里我们用的测试框架是JMH。这个文件在预热阶段会被操作系统加载到缓存中(在预热阶段会先压缩两次)。我会将内容压缩到ByteArrayOutputStream流中(我知道这并不是最快的方法,但是对于各个测试而言它的性能是比较稳定的,并且不需要花费时间将压缩后的数据写入到磁盘里),因此还需要一些内存空间来存储这个输出结果。

    下面是测试类的基类。所有的测试不同的地方都只在于压缩的输出流的实现不同,因此可以复用这个测试基类,只需从StreamFactory实现中生成一个流就好了:

    
    @OutputTimeUnit(TimeUnit.MILLISECONDS)
    @State(Scope.Thread)
    @Fork(1)
    @Warmup(iterations = 2)
    @Measurement(iterations = 3)
    @BenchmarkMode(Mode.SingleShotTime)
    public class TestParent {
        protected Path m_inputFile;
     
        @Setup
        public void setup()
        {
            m_inputFile = InputGenerator.FILE_PATH.toPath();
        }
     
        interface StreamFactory
        {
            public OutputStream getStream( final OutputStream underlyingStream ) throws IOException;
        }
     
        public int baseBenchmark( final StreamFactory factory ) throws IOException
        {
            try ( ByteArrayOutputStream bos = new ByteArrayOutputStream((int) m_inputFile.toFile().length());
                  OutputStream os = factory.getStream( bos ) )
            {
                Files.copy(m_inputFile, os);
                os.flush();
                return bos.size();
            }
        }
    }
    



    这些测试用例都非常相似(在文末有它们的源代码),这里只列出了其中的一个例子——JDK deflate的测试类;

    
    public class JdkDeflateTest extends TestParent {
        @Param({"1", "2", "3", "4", "5", "6", "7", "8", "9"})
        public int m_lvl;
     
        @Benchmark
        public int deflate() throws IOException
        {
            return baseBenchmark(new StreamFactory() {
                @Override
                public OutputStream getStream(OutputStream underlyingStream) throws IOException {
                    final Deflater deflater = new Deflater( m_lvl, true );
                    return new DeflaterOutputStream( underlyingStream, deflater, 512 );
                }
            });
        }
    }
    



    测试结果

    输出文件的大小


    首先我们来看下输出文件的大小:

    ||实现||文件大小(字节)||
    ||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||




    可以看出文件的大小相差悬殊(从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||





    我们再将压缩时间和文件大小合并到一个表中来统计下算法的吞吐量,看看能得出什么结论。

    吞吐量及效率

    ||实现||时间(ms)||未压缩文件大小||吞吐量(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||




    可以看到,其中大多数实现的效率是非常低的:在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/秒。

  • 源代码

    Java压缩测试源码




    原创文章转载请注明出处:http://it.deepinmind.com

    英文原文链接
    3
    1
    分享到:
    评论

    相关推荐

      java自带压缩方式的性能比较

      通过提供的代码文件`CompressTestMain.java`、`GzipUtils.java`和`ZipUtils.java`,我们可以推测作者可能构建了一个测试环境,比较了Gzip和Zip压缩算法在实际操作中的性能差异。 首先,让我们了解Gzip和Zip的基本...

      通过Java测试几种压缩算法的性能(附测试代码下载)

      在本文中,我们将探讨如何通过Java来测试不同的压缩算法,并分析它们的性能。实验中涉及了JDK内置的GZIP和Deflate算法,以及LZ4和Snappy这两种高效的第三方压缩算法。这些测试对于理解不同压缩算法在实际应用中的...

      java实现视频压缩

      同时,不同的压缩算法会直接影响到压缩质量和文件大小的平衡,因此在实际应用中,开发者需要根据具体需求来选择合适的压缩策略。 总的来说,通过Java实现视频压缩,不仅可以提高开发效率,还能灵活地适应各种应用...

      几种无损数据压缩算法的探讨及在java web程序中的应用.pdf

      本文主要探讨了几种常见的无损压缩算法,并分析了它们在Java Web程序中的应用。 一、Huffman编码(哈夫曼编码) Huffman编码是一种广泛使用的无损压缩算法,由David Huffman在1952年提出。其原理是根据字符在数据中...

      用Java实现的道格拉斯-普克压缩算法

      标签“Java”表明我们关注的是Java语言的实现,“道格拉斯”和“压缩算法”则是指具体的算法实现。在实现时,Java提供了丰富的数据结构(如ArrayList或LinkedList)来存储点序列,以及数学库(如java.awt.geom.Line...

      算术算法压缩实现Java

      以下是关于算术压缩算法及其Java实现的详细讲解。 算术编码的基本原理: 1. **概率模型**:在压缩前,对输入数据建立概率模型。每个可能的符号(例如,文本中的字符)被赋予一个概率,这个概率反映了该符号在数据流...

      java算法,实现压缩及解压缩

      ### Java算法:实现压缩及解压缩 #### 一、压缩功能实现 在Java中实现文件压缩功能主要依赖于`java.util.zip`包中的类。以下是对压缩代码的详细解析: ##### 1. 导入所需类库 ```java import java.io....

      各类压缩算法聚合

      本文将深入探讨“压缩算法 集合 java”这一主题,结合Java编程语言,阐述压缩算法的基本原理、常用算法以及如何在Java环境中实现和应用。 首先,压缩算法是一种用于减小数据量的技术,通过消除数据中的冗余信息,...

      压缩算法.docx

      压缩算法在IT行业中扮演着重要的角色,特别是在数据传输和存储方面。本文主要探讨了网络上主流的压缩算法,包括...在实际应用中,开发者需根据需求选择合适的压缩算法,同时考虑到性能、资源消耗和兼容性等因素。

      LZ78算法实现对任意字符串的压缩与解压

      LZ78压缩算法是一种基于字典的无损数据压缩方法,由Abraham Lempel、Jacob Ziv和Stu Arkin在1977年提出。它通过查找输入字符串中的最长匹配前缀来构建一个新的编码,从而实现数据的压缩。这种算法的主要思想是创建一...

      Burrows-Wheeler压缩算法JAVA实现

      Burrows-Wheeler转换(BWT)是一种数据预处理技术,常用于文本压缩算法,它由Michael Burrows和David Wheeler在1994年提出。这个算法的核心思想是通过重新排列输入字符串中的字符顺序,使其形成高度重复的模式,从而...

      Google新开源压缩算法Brotli

      标题和描述中提到的知识点包括了Brotli压缩算法及其与Deflate、Zopfli、LZMA、LZHAM和Bzip2压缩算法的比较。...同时,也提供了对其他几种主流压缩算法性能的评估,以及在不同应用场景下选择合适压缩算法的参考依据。

      JAVA文件压缩与解压缩实践(源代码+论文)

      论文部分可能深入探讨了压缩算法的原理、性能比较、不同库的优缺点以及在实际项目中的应用策略。它可能还会涵盖如何优化压缩过程,减少内存消耗,以及在并发环境下的最佳实践。 综上所述,"JAVA文件压缩与解压缩...

      22、MapReduce使用Gzip压缩、Snappy压缩和Lzo压缩算法写文件和读取相应的文件

      总结来说,MapReduce支持多种压缩算法,包括Gzip、Snappy和Lzo,以适应不同场景的需求。在处理大规模数据时,合理选择和使用压缩算法可以显著优化存储和计算效率。同时,了解各种压缩算法的特点和性能,对于优化...

      LZ77压缩,js&java版本

      在JavaScript和Java这两种不同的编程语言中,LZ77压缩算法可以被用于处理大文本的上传和解压,以减少网络传输的数据量,提高传输效率。 **算法原理** LZ77的核心思想是查找输入数据中的重复模式并用更短的编码来...

      java数据压缩传输

      Java标准库中的`java.util.zip`包提供了`GZIPOutputStream`和`GZIPInputStream`类,它们实现了广泛使用的Gzip压缩算法。Gzip是一种高效的压缩格式,常用于文件传输。以下是如何使用这两个类进行数据压缩和解压缩的...

      压缩算法及其代码实现

      在IT领域,压缩算法是数据处理和存储的关键技术之一,其主要目的是减小文件的大小,从而节省存储空间和提高传输效率。LZW(Lempel-Ziv-Welch)压缩算法是一种广泛应用的无损数据压缩方法,尤其在文本、图像和音频...

      JAVA文件压缩与解压缩实践(源代码+论文).rar

      4. **论文**:论文部分可能详细讨论了文件压缩的原理、性能比较、不同压缩格式的选择策略以及在Java中实现压缩和解压缩的优化技巧。通过阅读论文,我们可以学习到理论知识,比如压缩率、时间复杂度和空间效率等。 5...

      java实现压缩与解压缩

      这些库提供了更强大、更高效的压缩算法和更多的文件格式支持。 6. **性能优化**: 在处理大量数据时,可以通过缓冲区优化读写操作,减少磁盘I/O次数。另外,使用多线程可以并行处理文件,提高压缩和解压缩的速度。...

    Global site tag (gtag.js) - Google Analytics