从单独压缩的块创建 gzip 流
Creating a gzip stream from separately compressed chunks
我希望能够使用并发 CPU 线程生成 gzip (.gz) 文件。即,我将使用单独初始化的 z_stream
记录从输入文件中压缩单独的块。
在经典的单线程操作中,zlib 的 inflate() 函数应该可以读取生成的文件。
这可能吗?即使它需要定制的 zlib 代码?唯一的要求是当前存在的 zlib 的膨胀代码可以处理它。
更新
pigz 源代码演示了它是如何工作的。它使用一些复杂的优化来在块之间共享字典,从而保持最佳压缩率。如果使用更新的 zlib 版本,它会进一步处理位打包。
但是,我喜欢了解如何自己动手,保持简单,不使用 pigz
使用的优化。
虽然许多人认为源代码是最终文档 (Ed Post, anyone?),但我宁愿用通俗易懂的语言对其进行解释以避免误解。 (虽然文档实际上很好地描述了发生的事情,但他们并没有很好地解释自己需要做什么。)
通过浏览代码,到目前为止我明白了:
看来人们只是使用 deflate(..., Z_SYNC_FLUSH)
而不是 Z_FINISH
创建每个压缩块。然而, deflateEnd()
给出了一个错误,不确定是否可以忽略。并且需要手动计算所有块的最终校验和,尽管我想知道如何在最后添加校验和。还有一个相当复杂的 put_trailer()
函数用于编写 gzip header - 我想知道是否也可以通过 zlib 自己的代码处理简单的情况?
如有任何澄清,我们将不胜感激。
此外,我意识到我应该包括询问以同样的方式编写 zlib 流,以便将 multithreaded-compressed 文件写入 zip 存档。我怀疑,由于缺少更复杂的 gzip header.
,可能会有更多的简化
当然..
A parallel implementation of gzip for modern
multi-processor, multi-core machines
答案就在你的问题中。每个线程都有自己的 deflate
实例来生成原始压缩数据(参见 deflateInit2()
),它压缩提供给它的数据块,以 Z_SYNC_FLUSH
而不是 Z_FINISH
结尾.除了最后一个数据块,它以 Z_FINISH
结尾。无论哪种方式,这都会在字节边界上结束每个压缩数据流。确保从 deflate()
中获取所有生成的数据。然后你可以连接所有的压缩数据流。 (以正确的顺序!)在其前面加上您自己生成的 gzip header。这样做很简单(参见 RFC 1952)。如果您不需要 header 中包含的任何附加信息(例如文件名、修改日期),它可以只是一个恒定的 10 字节序列。 gzip header 并不复杂。
您还可以计算同一线程或不同线程中每个未压缩块的 CRC-32,并使用 crc32_combine()
组合这些 CRC-32。 gzip 预告片需要它。
在写入所有压缩流后,以 Z_FINISH
结束的压缩流结束,您附加 gzip 尾部。所有这一切都是 four-byte CRC-32 和总未压缩长度的低四个字节,都是 little-endian 顺序。总共八个字节。
在每个线程中,您可以在完成每个块时使用 deflateEnd()
,或者如果您要为更多块重用线程,请使用 deflateReset()
。我在 pigz 中发现,在处理多个块时,让线程保持打开状态并在其中打开 deflate
个实例会更有效率。在关闭线程之前,只需确保对线程处理的最后一个块使用 deflateEnd()
。是的,来自 deflateEnd()
的错误可以忽略。只需确保您 运行 deflate()
直到 avail_out
不为零以获取所有压缩数据。
这样做,每个线程都会压缩其块,而不引用任何其他未压缩的数据,在这种情况下,此类引用通常会在串行执行时提高压缩率。如果你想更高级,你可以为每个线程提供未压缩数据块进行压缩,和前一个块的最后 32K 为压缩器提供历史记录。您使用 deflateSetDictionary()
.
执行此操作
更高级的是,您可以通过有时使用 Z_PARTIAL_FLUSH
直到到达字节边界来减少压缩流之间插入的字节数。有关详细信息,请参阅 pigz。
更高级但更慢的是,您可以在位级别而不是字节级别附加压缩流。这将需要将压缩流的每个字节移位两次以构建新的移位流。每八个前面的压缩流中至少有七个。这消除了压缩流之间插入的所有额外位。
可以用完全相同的方式生成 zlib 流,使用 adler32_combine()
作为校验和。
你关于 zlib 的问题暗示了一种混淆。 zip 格式不使用 zlib header 和预告片。 zip has its own structure,其中嵌入了原始压缩流。您也可以对那些原始压缩流使用上述方法。
我希望能够使用并发 CPU 线程生成 gzip (.gz) 文件。即,我将使用单独初始化的 z_stream
记录从输入文件中压缩单独的块。
在经典的单线程操作中,zlib 的 inflate() 函数应该可以读取生成的文件。
这可能吗?即使它需要定制的 zlib 代码?唯一的要求是当前存在的 zlib 的膨胀代码可以处理它。
更新
pigz 源代码演示了它是如何工作的。它使用一些复杂的优化来在块之间共享字典,从而保持最佳压缩率。如果使用更新的 zlib 版本,它会进一步处理位打包。
但是,我喜欢了解如何自己动手,保持简单,不使用 pigz
使用的优化。
虽然许多人认为源代码是最终文档 (Ed Post, anyone?),但我宁愿用通俗易懂的语言对其进行解释以避免误解。 (虽然文档实际上很好地描述了发生的事情,但他们并没有很好地解释自己需要做什么。)
通过浏览代码,到目前为止我明白了:
看来人们只是使用 deflate(..., Z_SYNC_FLUSH)
而不是 Z_FINISH
创建每个压缩块。然而, deflateEnd()
给出了一个错误,不确定是否可以忽略。并且需要手动计算所有块的最终校验和,尽管我想知道如何在最后添加校验和。还有一个相当复杂的 put_trailer()
函数用于编写 gzip header - 我想知道是否也可以通过 zlib 自己的代码处理简单的情况?
如有任何澄清,我们将不胜感激。
此外,我意识到我应该包括询问以同样的方式编写 zlib 流,以便将 multithreaded-compressed 文件写入 zip 存档。我怀疑,由于缺少更复杂的 gzip header.
,可能会有更多的简化当然..
A parallel implementation of gzip for modern multi-processor, multi-core machines
答案就在你的问题中。每个线程都有自己的 deflate
实例来生成原始压缩数据(参见 deflateInit2()
),它压缩提供给它的数据块,以 Z_SYNC_FLUSH
而不是 Z_FINISH
结尾.除了最后一个数据块,它以 Z_FINISH
结尾。无论哪种方式,这都会在字节边界上结束每个压缩数据流。确保从 deflate()
中获取所有生成的数据。然后你可以连接所有的压缩数据流。 (以正确的顺序!)在其前面加上您自己生成的 gzip header。这样做很简单(参见 RFC 1952)。如果您不需要 header 中包含的任何附加信息(例如文件名、修改日期),它可以只是一个恒定的 10 字节序列。 gzip header 并不复杂。
您还可以计算同一线程或不同线程中每个未压缩块的 CRC-32,并使用 crc32_combine()
组合这些 CRC-32。 gzip 预告片需要它。
在写入所有压缩流后,以 Z_FINISH
结束的压缩流结束,您附加 gzip 尾部。所有这一切都是 four-byte CRC-32 和总未压缩长度的低四个字节,都是 little-endian 顺序。总共八个字节。
在每个线程中,您可以在完成每个块时使用 deflateEnd()
,或者如果您要为更多块重用线程,请使用 deflateReset()
。我在 pigz 中发现,在处理多个块时,让线程保持打开状态并在其中打开 deflate
个实例会更有效率。在关闭线程之前,只需确保对线程处理的最后一个块使用 deflateEnd()
。是的,来自 deflateEnd()
的错误可以忽略。只需确保您 运行 deflate()
直到 avail_out
不为零以获取所有压缩数据。
这样做,每个线程都会压缩其块,而不引用任何其他未压缩的数据,在这种情况下,此类引用通常会在串行执行时提高压缩率。如果你想更高级,你可以为每个线程提供未压缩数据块进行压缩,和前一个块的最后 32K 为压缩器提供历史记录。您使用 deflateSetDictionary()
.
更高级的是,您可以通过有时使用 Z_PARTIAL_FLUSH
直到到达字节边界来减少压缩流之间插入的字节数。有关详细信息,请参阅 pigz。
更高级但更慢的是,您可以在位级别而不是字节级别附加压缩流。这将需要将压缩流的每个字节移位两次以构建新的移位流。每八个前面的压缩流中至少有七个。这消除了压缩流之间插入的所有额外位。
可以用完全相同的方式生成 zlib 流,使用 adler32_combine()
作为校验和。
你关于 zlib 的问题暗示了一种混淆。 zip 格式不使用 zlib header 和预告片。 zip has its own structure,其中嵌入了原始压缩流。您也可以对那些原始压缩流使用上述方法。