有人能推荐一下,我是应该做类似于这样的事情:
os = new GzipOutputStream(new BufferedOutputStream(...));
或者os = new BufferedOutputStream(new GzipOutputStream(...));
哪个更有效? 我应该使用BufferedOutputStream吗?
有人能推荐一下,我是应该做类似于这样的事情:
os = new GzipOutputStream(new BufferedOutputStream(...));
或者os = new BufferedOutputStream(new GzipOutputStream(...));
哪个更有效? 我应该使用BufferedOutputStream吗?
GZIPOutputStream已经内置了缓冲区,因此不需要在其后加上BufferedOutputStream。gojomo的出色答案已经提供了一些关于在哪里放置缓冲区的指导。
GZIPOutputStream的默认缓冲区大小仅为512字节,因此您将希望通过构造函数参数将其增加到8K甚至64K。BufferedOutputStream的默认缓冲区大小为8K,这就是当将默认的GZIPOutputStream和BufferedOutputStream组合在一起时可以测量出优势的原因。通过适当调整GZIPOutputStream内置的缓冲区大小也可以实现这种优势。
因此,回答您的问题:"我应该使用BufferedOutputStream吗?"→ 不,在您的情况下,不应使用它,而是将GZIPOutputStream的缓冲区设置为至少8K。
我应该先使用哪个
GzipOutputStream
和BufferedOutputStream
对于对象流,我发现将缓冲流包裹在输入和输出的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次测试,并剪掉了第一次运行并对其余的结果求平均。我还尝试了缓冲gzip缓冲,这对于对象来说稍微好一些,但对于文本来说则更差了。我没有改变缓冲区大小。
对于对象流,我测试了两个在10多兆字节范围内的序列化对象文件。 对于更大的文件(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%
缓冲有助于在最终数据目的地最好以比您的代码推送的更大块的方式进行读取/写入时。因此,通常希望将缓冲尽可能靠近要求较大块的地方。在您的示例中,这是省略的“...”,所以请使用GzipOutputStream包装BufferedOutputStream。同时,根据测试结果调整BufferedOutputStream缓冲区大小,使其与目标最佳匹配。
我怀疑外部的BufferedOutputStream如果有的话,不会对没有显式缓冲的情况产生太大帮助。为什么呢?不管外部缓冲是否存在,GzipOutputStream都会以相同大小的块将其写入“...”。因此,“...”无法进行优化;您只能使用GzipOutputStream write()函数的固定块大小。
还请注意,通过缓冲压缩数据而不是未压缩数据,可以更有效地利用内存。如果您的数据经常实现6倍压缩,则“内部”缓冲等效于6倍大的“外部”缓冲。
通常情况下,在使用FileOutputStream(假设这是...所代表的)时,您需要将缓冲区设置得比较靠近以避免过多地调用操作系统和频繁的磁盘访问。然而,如果您要向GZIPOutputStream写入大量小块,则可能也会从GZIPOS周围的缓冲区中受益。原因是GZIPOS中的write方法是同步的,这还涉及到其他几个同步调用和一些本地(JNI)调用(用于更新CRC32并执行实际压缩)。所有这些都会增加每次调用的额外开销。因此,在这种情况下,我建议您同时使用两个缓冲区会更有益。
阅读javadoc,您会发现BIS用于缓冲从某些原始源读取的字节。一旦获得所需的原始字节,您需要对其进行压缩,因此将BIS包装在GIS中。缓冲GZIP的输出毫无意义,因为需要考虑缓冲GZIP的问题,那么谁来做呢?
new GzipInputStream( new BufferedInputStream ( new FileInputXXX