【问题标题】:What order should I use GzipOutputStream and BufferedOutputStream我应该使用什么顺序 GzipOutputStream 和 BufferedOutputStream
【发布时间】:2009-07-04 14:41:05
【问题描述】:

谁能推荐我是否应该这样做:

os = new GzipOutputStream(new BufferedOutputStream(...));

os = new BufferedOutputStream(new GzipOutputStream(...));

哪个更有效率?我应该使用 BufferedOutputStream 吗?

【问题讨论】:

    标签: java gzipoutputstream


    【解决方案1】:

    GZIPOutputStream 已经带有内置缓冲区。因此,没有必要在链中将 BufferedOutputStream 放在它旁边。 gojomo 的出色回答已经为放置缓冲区的位置提供了一些指导。

    GZIPOutputStream 的默认缓冲区大小仅为 512 字节,因此您需要通过构造函数参数将其增加到 8K 甚至 64K。 BufferedOutputStream 的默认缓冲区大小为 8K,这就是为什么在组合默认 GZIPOutputStream 和 BufferedOutputStream 时可以衡量优势的原因。这一优势也可以通过适当调整 GZIPOutputStream 的内置缓冲区大小来实现。

    所以,回答你的问题:“我应该使用 BufferedOutputStream 吗?” → 不,在你的情况下,你不应该使用它,而是将 GZIPOutputStream 的缓冲区设置为至少 8K。

    【讨论】:

    • 这只是对了一半:GZIPOutputStream 内部输出缓冲区决定了一次可以压缩多少字节,但如果你输入小块字节(或单个字节),它仍然会写入一旦 Deflater 可以发出一个压缩数据块,即使输出缓冲区可以容纳更多数据,也会立即支持输出流。但是,如果您在 GZIPOutputStream 前面放置第二个缓冲区,这很好,确保只写入大块。所以答案还是正确的,只是细节比那复杂一点。
    【解决方案2】:

    当数据的最终目的地最好以更大的块读取/写入时,缓冲会有所帮助,而不是您的代码会以其他方式推送它。因此,您通常希望缓冲尽可能接近想要更大块的位置。在您的示例中,这是省略的“...”,因此将 BufferedOutputStream 与 GzipOutputStream 包装起来。并且,调整 BufferedOutputStream 缓冲区大小以匹配测试显示最适合目标的内容。

    我怀疑外部的 BufferedOutputStream 在没有显式缓冲的情况下会有所帮助(如果有的话)。为什么不?无论外部缓冲是否存在,GzipOutputStream 都会以相同大小的块对“...”执行其 write()。所以不可能对“...”进行优化;您被 GzipOutputStream write() 的大小所困扰。

    另请注意,通过缓冲压缩数据而不是未压缩数据,您可以更有效地使用内存。如果您的数据经常实现 6 倍压缩,则“内部”缓冲区相当于 6 倍大的“外部”缓冲区。

    【讨论】:

      【解决方案3】:

      通常,您希望在 FileOutputStream 附近有一个缓冲区(假设这就是 ... 所代表的内容),以避免过多调用操作系统和频繁访问磁盘。但是,如果您正在向 GZIPOutputStream 写入大量小块,您也可能会从 GZIPOS 周围的缓冲区中受益。原因是 GZIPOS 中的 write 方法是同步的,并且还导致很少的其他同步调用和几个本机 (JNI) 调用(更新 CRC32 并进行实际压缩)。这些都增加了每次调用的额外开销。所以在这种情况下,我会说你将从这两个缓冲区中受益。

      【讨论】:

        【解决方案4】:

        GzipOutputStreamBufferedOutputStream应该使用什么顺序

        对于对象流,我发现将缓冲流包裹在 gzip 流周围以用于输入和输出几乎总是明显更快。对象越小,效果越好。在所有情况下都比没有缓冲流更好或相同。

        ois = new ObjectInputStream(new BufferedInputStream(new GZIPInputStream(fis)));
        oos = new ObjectOutputStream(new BufferedOutputStream(new GZIPOutputStream(fos)));
        

        然而,对于文本和直接字节流,我发现这是一个折腾——缓冲流周围的 gzip 流只是稍微好一点。但在所有情况下都比没有缓冲流更好。

        reader = new InputStreamReader(new GZIPInputStream(new BufferedInputStream(fis)));
        writer = new OutputStreamWriter(new GZIPOutputStream(new BufferedOutputStream(fos)));
        

        每个版本我运行了 20 次,并切断了第一次运行并对其余的进行平均。我还尝试了 buffered-gzip-buffered,它对对象稍微好一点,对文本更差。我根本没有玩过缓冲区大小。


        对于对象流,我测试了 10 兆字节的 2 个序列化对象文件。对于较大的文件(38mb),读取速度提高了 85%(0.7 对 5.6 秒),但写入速度实际上稍慢(5.9 对 5.7 秒)。这些对象中有一些大型数组,这可能意味着更大的写入。

        method       crc     date  time    compressed    uncompressed  ratio
        defla   eb338650   May 19 16:59      14027543        38366001  63.4%
        

        对于较小的文件 (18mb),读取速度提高了 75%(1.6 对 6.1 秒),写入速度提高了 40%(2.8 对 4.7 秒)。它包含大量的小物体。

        method       crc     date  time    compressed    uncompressed  ratio
        defla   92c9d529   May 19 16:56       6676006        17890857  62.7%
        

        对于文本阅读器/编写器,我使用了 64mb 的 csv 文本文件。缓冲流周围的 gzip 流读取速度提高了 11%(950 对 1070 毫秒),写入速度稍快(7.9 对 8.1 秒)。

        method       crc     date  time    compressed    uncompressed  ratio
        defla   c6b72e34   May 20 09:16      22560860        63465800  64.5%
        

        【讨论】:

        • 如果能看到准确的输入数据和标准差就好了,否则结果无法重现
        【解决方案5】:

        我建议你尝试一个简单的基准来计算压缩一个大文件需要多长时间,看看它是否有很大的不同。 GzipOutputStream 确实有缓冲,但它是一个较小的缓冲区。我会用 64K 缓冲区做第一个,但你可能会发现两者都做更好。

        【讨论】:

          【解决方案6】:

          阅读 javadoc,您会发现 BIS 用于缓冲从某些原始源读取的字节。一旦你得到你想要压缩的原始字节,你就可以用 GIS 包装 BIS。缓冲 GZIP 的输出是没有意义的,因为需要考虑缓冲 GZIP 会怎样,谁来做呢?

          new GzipInputStream( new BufferedInputStream ( new FileInputXXX
          

          【讨论】:

          • “你想压缩它们,所以你用 GIS 包装 BIS” - GIS 不压缩。它解压缩。 FWIW 我很难理解你在答案后半部分的一般观点。
          猜你喜欢
          • 2018-02-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-09-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多