Эффективность GZipStream - PullRequest
       8

Эффективность GZipStream

7 голосов
/ 28 сентября 2011

Я пытаюсь сохранить большой массив UInt16 в файл. positionCnt - около 50000, stationCnt - около 2500. Сохраненный напрямую, без GZipStream, файл имеет размер около 250 МБ, который может быть сжат с помощью внешней zip-программы до 19 МБ. С помощью следующего кода файл составляет 507 МБ. Что я делаю не так?

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();

Ответы [ 2 ]

12 голосов
/ 29 сентября 2011

Не уверен, на какой версии .NET вы работаете. В более ранних версиях он использовал размер окна, равный размеру буфера, из которого вы писали. Так что в вашем случае он будет пытаться сжать каждое целое число в отдельности. Я думаю они изменили это в .NET 4.0, но не подтвердили это.

В любом случае вам нужно создать буферный поток перед GZipStream:

// Создание потока файлов с буфером 64 КБ FileStream fs = new FileStream (имя файла, 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 Кбайта и может гораздо лучше выполнять сжатие.

Буферы размером более 64 КБ не дадут вам лучшего сжатия.

3 голосов
/ 29 сентября 2011

По какой-то причине, которая не очевидна для меня при быстром чтении реализации GZip в .Net, производительность чувствительна к объему данных, записываемых за один раз.Я сравнил ваш код с несколькими стилями записи в 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)
}
...