从单独压缩的块

时间:2016-07-01 11:10:46

标签: compression gzip zlib

我希望能够使用并发CPU线程生成gzip(.gz)文件。即,我将使用单独初始化的z_stream记录从输入文件中缩小单独的块。

在经典的单线程操作中,zlib的inflate()函数可以读取生成的文件。

这可能吗?即使它需要定制的zlib代码?唯一的要求是当前存在的zlib的inflate代码可以处理它。

更新

pigz源代码演示了它的工作原理。它使用一些复杂的优化来在块之间共享字典,从而保持压缩率最佳。如果使用更新的zlib版本,它还会处理位打包。

但是,如果没有pigz使用的优化,我想了解如何自己动手,保持简单。

虽然许多人认为源代码是最终文档(Ed Post, anyone?),但我宁愿用简单的词语解释它以避免误解。 (虽然文档实际上描述了发生得非常好的事情,但他们并没有很好地解释需要做些什么来推动自己的事情。)

从浏览代码开始,我到目前为止已经找到了这个:

似乎只使用deflate(..., Z_SYNC_FLUSH)创建每个压缩块而不是使用Z_FINISH。但是,deflateEnd()会出错,不确定是否可以忽略。并且需要手动计算所有块的最终校验和,但我想知道如何在最后添加校验和。还有一个相当复杂的put_trailer()函数用于编写gzip头 - 我想知道zlib自己的代码是否也可以用简单的情况来处理它?<​​/ p>

对此有任何澄清表示赞赏。

另外,我意识到我应该以同样的方式包括询问编写zlib流,以便将多线程压缩文件写入zip存档。在那里,我怀疑,由于缺少更复杂的gzip标题,可能会有更多的简化。

2 个答案:

答案 0 :(得分:3)

答案在你的问题中。每个线程都有自己的deflate实例来生成原始deflate数据(请参阅deflateInit2()),该数据压缩馈送给它的数据块,以Z_SYNC_FLUSH而不是Z_FINISH结尾。除了以Z_FINISH结尾的最后一块数据。无论哪种方式,这都会在字节边界上结束每个得到的压缩数据流。确保从deflate()中获取所有生成的数据。然后,您可以连接所有压缩数据流。 (按照正确的顺序!)使用您自己生成的gzip标头。这样做很简单(见RFC 1952)。如果您不需要标题中包含任何其他信息(例如文件名,修改日期),它可以只是一个10字节的常量序列。 gzip标头并不复杂。

您还可以在同一个线程或不同的线程中计算每个未压缩块的CRC-32,并使用crc32_combine()组合这些CRC-32。你需要gzip预告片。

写完所有压缩流后,以Z_FINISH结束的压缩流结束,然后附加gzip预告片。所有这一切都是四字节CRC-32和总未压缩长度的低四字节,都是小端序。总共八个字节。

在每个线程中,您可以在完成每个块时使用deflateEnd(),或者如果要重新使用线程获取更多块,请使用deflateReset()。我在pigz中发现,在处理多个块时,保持线程打开并在其中打开deflate实例效率更高。在关闭线程之前,确保使用deflateEnd()作为线程处理的最后一个块。是的,可以忽略来自deflateEnd()的错误。只需确保您运行deflate(),直到avail_out不为零才能获取所有压缩数据。

这样做,每个线程压缩其块而不引用任何其他未压缩数据,这些引用通常会在串行执行时改进压缩。如果您想获得更高级的功能,可以为每个线程提供要压缩的未压缩数据块,上一个块的最后32K,以便为压缩器提供历史记录。您可以使用deflateSetDictionary()执行此操作。

更高级的是,您可以通过有时使用Z_PARTIAL_FLUSH来减少压缩流之间插入的字节数,直到达到字节边界。有关详细信息,请参阅pigz。

更高级但速度更慢,您可以在位级别而不是字节级别附加压缩流。这将需要将压缩流的每个字节移位两次以构建新的移位流。每八个先前压缩流中至少有七个。这消除了在压缩流之间插入的所有额外位。

zlib流可以完全相同的方式生成,使用adler32_combine()作为校验和。

关于zlib的问题意味着混乱。 zip格式不使用zlib标头和预告片。 zip has its own structure,其中嵌入了原始的deflate流。您也可以将上述方法用于那些原始的deflate流。

答案 1 :(得分:1)

不确定..

http://zlib.net/pigz/

  

现代gzip的并行实现   多处理器,多核机器