【问题标题】:C++/C Multiple threads to read gz file simultaneouslyC++/C多线程同时读取gz文件
【发布时间】:2023-07-11 05:51:01
【问题描述】:

我正在尝试从多个线程读取 gzip 压缩文件。

我认为这会显着加快解压缩过程,因为我在多个线程中的 gzread 函数从不同的文件偏移量开始(使用 gseek),因此它们读取文件的不同部分。

简化后的代码是这样的

// in threads
auto gf = gzopen("file.gz",xxx);
gzseek(gf,offset);
gzread(xx);
gzclose(gf);

令我惊讶的是,我的多线程版本程序确实没有加速。 20线程版本使用的时间与单线程版本完全相同。我很确定这远离磁盘瓶颈。

我猜 zlib 膨胀功能可能需要解压缩整个文件才能读取一小部分,但我没有从他们的手册中得到任何线索。

有人知道如何加快速度吗?

【问题讨论】:

  • 通常大部分时间都花在数据读取上。不是在减压。将您的数据放在可用的最快媒体上,然后重试。
  • @GMichael 感谢您提供线索。我做了实验,我为两个压缩文件运行相同的程序,一个压缩级别较高,大小为 600M,另一个压缩级别较低,但大小为 2G。第一个文件用了 22 秒,而后者用了 10 秒。所以更有可能是减压的原因。
  • 也许尝试在每个线程中使用相同的文件句柄而不是单独打开它
  • @fandin 如果是这种情况,请检查 CPU 上的负载。您应该看到所有过去的 CPU 都在性能方面受益。顺便说一句,不要打开太多线程。上下文切换需要时间。
  • @fanbin 尝试为每个线程复制gf 结构

标签: c++ multithreading zlib compression deflate


【解决方案1】:

简短回答:由于 deflate 流的串行性质,gzseek() 必须解码从开始到请求的搜索点的所有压缩数据。因此,您无法从尝试做的事情中获得任何收益。事实上,所花费的总周期将随着压缩数据长度的平方而增加!所以不要那样做。

【讨论】:

    【解决方案2】:

    tl;dr:zlib 不是为随机访问而设计的。 It seems possible to implement,虽然需要完全通读才能构建索引,所以它可能对您的情况没有帮助。

    让我们看看zlib sourcegzseekgzseek64 的包装,其中包含:

    /* if within raw area while reading, just go there */
    if (state->mode == GZ_READ && state->how == COPY &&
            state->x.pos + offset >= 0) {
    

    如果我们正在处理一个 gzip 压缩文件,“在原始区域内”听起来不太正确。我们在gzguts.h中查一下state->how的含义:

    int how; /* 0: get header, 1: copy, 2: decompress */
    

    没错。在gz_open 结束时,对gz_reset 的调用将how 设置为0。返回到gzseek64,我们最终对状态进行了以下修改:

    state->seek = 1;
    state->skip = offset;
    

    gzread,当被调用时,通过调用gz_skip来处理这个:

    if (state->seek) {
        state->seek = 0;
        if (gz_skip(state, state->skip) == -1)
            return -1;
    }
    

    沿着这个兔子洞稍远一点,我们发现gz_skip 调用gz_fetch 直到gz_fetch 处理了足够的输入以进行所需的搜索。 gz_fetch 在其第一次循环迭代中调用 gz_look 设置 state->how = GZIP,这导致 gz_fetch 从输入解压缩数据。换句话说,您的怀疑是正确的:当您使用 gzseek 时,zlib 确实解压缩了整个文件。

    【讨论】:

    • 我的教科书极客/黑客示例。谢谢
    【解决方案3】:

    zlib 实现没有多线程(http://www.zlib.net/zlib_faq.html#faq21 - “zlib 线程安全吗? - 是的。...当然,您一次只能对来自单个线程的任何给定 zlib 或 gzip 流进行操作。”)并将“整个文件”解压缩到寻找的位置。

    并且 zlib 格式的对齐错误(位对齐)/没有偏移字段 (deflate format) 以启用并行解压缩/搜索。

    您可以尝试 z 的其他实现(deflate/inflate),例如,http://zlib.net/pigz/(或者从单核时代的古代压缩切换到非 zlib 现代并行格式,xz/lzma/google 的东西)

    pigz 代表 gzip 的并行实现,是 gzip 的全功能替代品,它在压缩数据时充分利用了多个处理器和多个内核。 pigz 由 Mark Adler 编写,使用 zlib 和 pthread 库。要编译和使用 pigz,请阅读源代码分发中的 README 文件。你可以阅读pigz manual page here.

    手册页是http://zlib.net/pigz/pigz.pdf,里面有很多有用的信息。

    它使用与zlib兼容的格式,但采用并行压缩:

    每个部分原始 deflate 流都由一个空的存储块终止...以便在字节边界处结束该部分比特流。

    不过,DEFLATE 格式不利于并行解压:

    解压缩不能并行化,至少在没有为此目的专门准备的 deflate 流的情况下不能并行化。结果,pigz使用单线程(主线程)进行解压,但是会创建另外三个线程进行读、写和校验计算,在某些情况下可以加快解压速度。

    【讨论】: