30

谁能推荐我是否应该做类似的事情:

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

或者

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

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

4

6 回答 6

30

我应该使用什么顺序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%
于 2011-05-19T22:12:38.683 回答
30

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

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

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

于 2013-09-26T15:30:12.120 回答
12

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

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

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

于 2009-07-04T22:49:38.000 回答
4

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

于 2013-08-14T20:44:30.817 回答
1

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

于 2009-07-04T14:49:33.283 回答
0

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

new GzipInputStream( new BufferedInputStream ( new FileInputXXX
于 2009-07-04T23:10:54.823 回答