GZipStream效率

7

我正试图将一个大的UInt16数组保存到文件中。positionCnt大约为50000,stationCnt大约为2500。如果直接保存而不使用GZipStream,则文件大小约为250MB,可以通过外部压缩程序压缩到19MB。使用以下代码后,文件大小为507MB。我做错了什么?

GZipStream cmp = new GZipStream(File.Open(cacheFileName, FileMode.Create), CompressionMode.Compress);
BinaryWriter fs = new BinaryWriter(cmp);
fs.Write((Int32)(positionCnt * stationCnt));
for (int p = 0; p < positionCnt; p++)
{
    for (int s = 0; s < stationCnt; s++)
    {
       fs.Write(BoundData[p, s]);
    }
}
fs.Close();

如果你使用gzip进行外部压缩,会是什么样子? - Jon Skeet
外部gzip大约为19.5 MB;bzip2略小于8MB。 - danatel
2个回答

12

不确定您正在运行的.NET版本。在早期版本中,它使用与您写入缓冲区相同大小的窗口大小。因此,在您的情况下,它会尝试逐个压缩每个整数。我认为他们在.NET 4.0中改变了这一点,但尚未验证。

无论如何,您想要做的是在GZipStream之前创建缓冲流:

// 创建带有64 KB缓冲区的文件流 FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None, 65536); GZipStream cmp = new GZipStream(fs, CompressionMode.Compress); …

GZipStream cmp = new GZipStream(File.Open(cacheFileName, FileMode.Create), CompressionMode.Compress);
BufferedStream buffStrm = new BufferedStream(cmp, 65536);
BinaryWriter fs = new BinaryWriter(buffStrm);

这样做,GZipStream 每次以 64KB 的数据块获取数据,可以更好地进行压缩。

大于 64KB 的缓冲区不会提供更好的压缩效果。


谢谢您的建议。但是它并没有起到帮助作用。使用更大的缓冲区得到的结果大约相同(517MB - 我还改变了数组的内容以加快实验速度)。此外,在您的示例中使用的名称fs存在问题 - fs是BinnaryFormatter(这是我的错误,我使用的名称fs和cmp很容易混淆)。 - danatel
这种优化(使用BufferedStream)在解压缩时也适用吗? - rollsch
@rolls 是的,缓冲流会提高解压性能。 - Jim Mischel
如果字节数组已经完全在内存中,那会怎样呢?那就不会有什么好处了,对吧? - rollsch
@rolls 缓冲流可以提高磁盘写入或读取速度。如果你只是在内存中工作,使用缓冲流可能会拖慢速度。 - Jim Mischel
显示剩余4条评论

3
出于某些原因,在快速阅读 .Net 中的GZip实现时,我不清楚性能对一次写入的数据量非常敏感。 我对您的代码进行了基准测试,与几种写入 GZipStream 的方式进行了比较,并发现最有效的版本是将长步幅写入磁盘。
在这种情况下需要权衡的是内存,因为您需要根据所需的步幅长度将short[,] 转换为byte[]
using (var writer = new GZipStream(File.Create("compressed.gz"),
                                   CompressionMode.Compress))
{
    var bytes = new byte[data.GetLength(1) * 2];
    for (int ii = 0; ii < data.GetLength(0); ++ii)
    {
        Buffer.BlockCopy(data, bytes.Length * ii, bytes, 0, bytes.Length);
        writer.Write(bytes, 0, bytes.Length);
    }

    // Random data written to every other 4 shorts
    // 250,000,000 uncompressed.dat
    // 165,516,035 compressed.gz (1 row strides)
    // 411,033,852 compressed2.gz (your version)
}

谢谢您的建议。我不知道您在基准测试中使用了什么数组内容。我的内容非常规律,可以压缩到8MB。165MB太大了。 - danatel
对于一半的数据(约125MB),使用data[ii, jj] = random.Next()进行赋值。我只是在指出使用1行或1列时压缩的差异。 - user7116
这解释了区别 - 随机噪声不像我的相当规则的数据那样可压缩。谢谢你的帮助。 - danatel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接