如何使用多线程进行zlib压缩

5
我有一大块数据(约2 GB),需要使用zlib(deflate())进行压缩。我目前每次读取500 kb的数据,进行压缩并将其写入输出文件。当只有1个线程时,一切正常。数据被压缩,我可以写入并解压回来。但当有2个线程时,程序在deflate()调用时挂起。以下是我两个zlib压缩线程调用的函数概述。
static z_stream z_str;

zlib_compress(...., bool last, bool first)
{

    if (first)
        deflateInit(&z_str, Z_DEFAULT_COMPRESSION);

    if (last)
        flush = Z_FINISH;
    else
        flush = Z_SYNC_FLUSH;

....
....
    status = deflate(&z_str, flush);
...
...
    if (last)
        deflateEnd(&z_str);

}

据我理解,在调用deflate()时,这两个调用都引用了同一个zstream,导致了不良行为。

我尝试将z_str作为本地变量,并相应地修改代码。但在解压缩时,它假定文件的总大小为512,而实际上只是第一块数据。

有什么想法可以实现这一点吗?


你的代码似乎只为每次调用zlib_compress()调用一次deflate()。 你在每个线程中多次调用zlib_compress函数吗?如果是这样,那么deflateInit()和deflateEnd不应该成为zlib_compress()调用的一部分;相反,它们应该仅在处理序列的开头和结尾(对于每个线程)执行。 - Jeremy Friesner
@JeremyFriesner 我已经删除了大部分代码,以便更简单地理解。是的,我目前只调用它们一次,但不是每个线程都调用。在压缩第一个块(512 KB)数据时,我调用deflateInit(),并在压缩最后一块数据(<=512 KB)时调用deflateEnd()。对于压缩,两个线程都引用相同的z流并尝试压缩数据。在此过程中,进程在deflate()调用中挂起。我应该尝试为每个线程调用它们吗?现在我在想,是否真的可以使用多个线程进行压缩? - Sandeep
@JeremyFriesner 非常感谢。你能把它发布为答案吗?我想接受它。 - Sandeep
好的,我已经将我的评论转换为答案。 - Jeremy Friesner
2个回答

5
我理解,当调用deflate()时,两个调用都引用了同一个zstream,导致了不良行为。
你期望发生什么?
每个线程需要自己的z_stream结构来使用。两个线程同时访问同一个z_stream是没有意义的。

2
你为什么不直接问你想知道的问题呢?如果你想知道如何利用多个核心加速压缩,那就请直接问。Jeremy回答的最后两段是不正确的。 - Mark Adler
1
不,没有库版本。它使用zlib,因此您可以查看pigz的源代码并使用相同的方法,或者根据需要调整pigz源代码。 - Mark Adler
谢谢 Mark。在你的评论之后,我又读了一遍RFC,发现标志中的DICT字段未设置,因此没有字典。但是我还没有成功。从压缩流中,最后四个字节是adler值。因此,我正在为每个作业调用COMB()并使用我的压缩流的最后4个字节。但不幸的是,如果一个工作只有一个作业,我的逻辑就可以正常工作。但是当我结合(使用COMB())两个或更多作业的adler并更新它时,它就无法工作。反压缩失败。我也尝试了adler32_comb()。结果相同。我没有将每个作业的adler写入输出文件。 - Sandeep
1
由于我们处于大数据时代,如果pigz被提升为一个库,那么它将在大数据程序员中非常受欢迎。是否有任何隐藏的陷阱阻止这种情况发生?如果没有,我可以尝试一下。 - Kemin Zhou
1
除了我的时间之外,唯一的障碍是接口。并行压缩库的用户会期望应用程序员如何使用它? - Mark Adler
显示剩余10条评论

5
可以同时压缩多个线程的数据,只要每个线程都有自己独立的z_stream对象。每个z_stream对象应该调用deflateInit(),然后根据需要调用多次deflate(),最后在将所有未压缩的数据传递给deflate()之后调用deflateEnd()。使用这种技术,例如同时压缩两个不同的文件将变得简单明了。
但是我怀疑你试图加速单个大文件的压缩,对吗?在这种情况下,你会发现至少不能以明显的方式实现。原因是压缩流的后面字节取决于该流的前面字节,这意味着直到生成所有前面的字节之后才能生成它们,这就排除了并行生成压缩文件的后半部分和前半部分。
你可以生成两个单独的压缩文件;一个是未压缩文件前一半的压缩内容,另一个是未压缩文件后一半的压缩内容。这可以并行完成,因为两个压缩流完全独立于彼此。请注意,您随后需要编写自己的例程来解压缩这两个文件并将结果连接回一个未压缩的文件,因为标准的压缩/解压缩工具不会意识到这种分而治之的技巧。
正如zlib的原始作者(Adler)所指出的那样,可以像pigz一样并行压缩大块数据。基本上,你需要在特定块前提供32K的未压缩数据。
==Chunk 1===
       -32K-====Chunk 2=======
                       --32K--====Chunk 3====

然后您可以组合压缩的数据。


5
不,使用_n_个处理器/核心可以将压缩加速_n_倍。这就是pigz的作用。每个线程都会被提供要压缩的数据部分和该部分之前32K未压缩数据。这32K足够进行压缩上下文。所有压缩流可以并行生成,然后再组合。 - Mark Adler
请在Mark Adler的评论中工作(首选),或者删除答案,因为它在很大程度上是错误的! - codeling
如果@MarkAdler把他的评论发布为答案,我会删除我的答案;我不想试图转述他的话,但我也不想删除我的答案,因为他的评论已经附在上面了。 - Jeremy Friesner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接