我正在尝试将大的UInt16数组保存到文件中。 positionCnt大约是50000,stationCnt大概是2500.直接保存,没有GZipStream,文件大约250MB,可以通过外部zip程序压缩到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();
答案 0 :(得分: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
以64 Kbyte块的形式获取数据,并且可以更好地进行压缩。
大于64KB的缓冲区不会给你更好的压缩。
答案 1 :(得分: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)
}