如何在非常大的输入上没有中间文件的情况下使用 GZipStream 进行多线程压缩/解压缩
How to do multithreaded compression / decompression with GZipStream without intermediate files on very large input
我想编写一个使用 NET 3.5 和 GZipStream 库进行多线程压缩/解压缩的程序。
输入文件非常大(假设有数百 GB)
我想在没有任何中间文件的情况下实现这一点。这是我最初的方法,但要求发生了变化。
我正在考虑以下方法并想验证这在纸面上看起来是否不错:
从源文件中读取并在内存中将其拆分为 constant-sized 个块。
由于内存有限,请跟踪线程数。
每个块都由单独的线程在内存中压缩。
这些压缩块按正确的顺序被推入 queue。
有一个线程从 queue 中读取并将其连接到输出文件中。
还在某处存储一些关于压缩块的元数据,稍后将放入 header。我想用这个来解压
完成以上我对多线程解压的想法就是:
读取有关串联块的元数据文件。
以元数据定义的块的形式从压缩文件中读取数据。
每个块在内存中由单独的线程解压缩。
这些解压的块按正确的顺序添加到queue。
有一个线程将解压缩的块连接成一个统一的输出文件。
以上看起来合理吗?
我不认为 GZip 可以这样分解。整个流在开始时取决于一些令牌字典(Huffman tree 或变体)。作为提示,GZipStream.CanSeek()
总是 returns 错误。
所以你的第 3 点会失败 - 块不是独立的。
并行处理 2 个甚至 3 个文件可能有效,具体取决于您 I/O 硬件。与较旧的 HDD 相比,更适合快速 SSD。网络 I/O 通常属于慢速 HDD。
是的,当您将每个块视为一个独立的项目(它拥有自己的 GZip 流)时,这应该可行。但这会增加一些开销,您的整体压缩率会降低一些。
对于每个块,您需要大小和序列号来反序列化和重新排序。
接收方无论如何都必须重新排序,因此您可以在发送方跳过它。
但是很难估计您会因此得到多少,压缩有点 CPU 密集但仍然比大多数 I/O 设备快得多。
当然可以。碰巧的是,有效 gzip 文件的串联也是一个有效的 gzip 文件。每个不同的可解压缩流称为 gzip 成员。您的元数据只需要文件中每个流开始的偏移量。
gzip 的额外块 header 限制为 64K 字节,因此这可能会限制块的大小,例如大约几十到一百兆字节。出于另一个原因,我建议您要压缩的数据块每个至少要有几兆字节——以避免压缩效率降低。
串联的一个缺点是您无法全面检查输入的完整性。例如,如果您以某种方式弄乱了成员的顺序,那么在解压时将不会检测到这一点,因为无论顺序如何,每个成员的完整性检查都会通过。因此,您可能希望包括对未压缩数据的全面检查。一个例子是整个未压缩数据的 CRC,可以使用 zlib 的 crc32_combine()
.
从成员的 CRC 计算出来
我很想知道在您的情况下,并行解压缩是否显着加快了速度。解压缩通常足够快,可以I/O绑定到正在读取的大容量存储设备上。
我想编写一个使用 NET 3.5 和 GZipStream 库进行多线程压缩/解压缩的程序。
输入文件非常大(假设有数百 GB)
我想在没有任何中间文件的情况下实现这一点。这是我最初的方法,但要求发生了变化。
我正在考虑以下方法并想验证这在纸面上看起来是否不错:
从源文件中读取并在内存中将其拆分为 constant-sized 个块。
由于内存有限,请跟踪线程数。
每个块都由单独的线程在内存中压缩。
这些压缩块按正确的顺序被推入 queue。
有一个线程从 queue 中读取并将其连接到输出文件中。
还在某处存储一些关于压缩块的元数据,稍后将放入 header。我想用这个来解压
完成以上我对多线程解压的想法就是:
读取有关串联块的元数据文件。
以元数据定义的块的形式从压缩文件中读取数据。
每个块在内存中由单独的线程解压缩。
这些解压的块按正确的顺序添加到queue。
有一个线程将解压缩的块连接成一个统一的输出文件。
以上看起来合理吗?
我不认为 GZip 可以这样分解。整个流在开始时取决于一些令牌字典(Huffman tree 或变体)。作为提示,GZipStream.CanSeek()
总是 returns 错误。
所以你的第 3 点会失败 - 块不是独立的。
并行处理 2 个甚至 3 个文件可能有效,具体取决于您 I/O 硬件。与较旧的 HDD 相比,更适合快速 SSD。网络 I/O 通常属于慢速 HDD。
是的,当您将每个块视为一个独立的项目(它拥有自己的 GZip 流)时,这应该可行。但这会增加一些开销,您的整体压缩率会降低一些。
对于每个块,您需要大小和序列号来反序列化和重新排序。
接收方无论如何都必须重新排序,因此您可以在发送方跳过它。
但是很难估计您会因此得到多少,压缩有点 CPU 密集但仍然比大多数 I/O 设备快得多。
当然可以。碰巧的是,有效 gzip 文件的串联也是一个有效的 gzip 文件。每个不同的可解压缩流称为 gzip 成员。您的元数据只需要文件中每个流开始的偏移量。
gzip 的额外块 header 限制为 64K 字节,因此这可能会限制块的大小,例如大约几十到一百兆字节。出于另一个原因,我建议您要压缩的数据块每个至少要有几兆字节——以避免压缩效率降低。
串联的一个缺点是您无法全面检查输入的完整性。例如,如果您以某种方式弄乱了成员的顺序,那么在解压时将不会检测到这一点,因为无论顺序如何,每个成员的完整性检查都会通过。因此,您可能希望包括对未压缩数据的全面检查。一个例子是整个未压缩数据的 CRC,可以使用 zlib 的 crc32_combine()
.
我很想知道在您的情况下,并行解压缩是否显着加快了速度。解压缩通常足够快,可以I/O绑定到正在读取的大容量存储设备上。