C++/C 多个线程同时读取 gz 文件

C++/C Multiple threads to read gz file simultaneously

本文关键字:读取 gz 文件 线程 C++      更新时间:2023-10-16

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

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

简化的代码就像

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

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

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

有人知道如何在我的情况下加快速度吗?

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

dr:zlib 不是为随机访问而设计的。似乎可以实现,但需要完整的通读才能构建索引,因此在您的情况下可能没有帮助。

Gzseek是gzseek64的包装器,其中包含:

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

如果我们正在处理一个压缩文件,"在原始区域内"听起来不太正确。让我们在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 确实会解压缩整个文件。

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

并且 zlib 格式具有错误的对齐(位对齐(/没有偏移场(放气格式(以实现并行解压缩/查找。

你可以尝试z(deflate/inflate(的另一个实现,例如 http://zlib.net/pigz/(或者从单核时代的古代压缩切换到非zlib现代并行格式,xz/lzma/谷歌的东西(

Pigz代表gzip的

并行实现,是gzip的全功能替代品,在压缩数据时利用多个处理器和多个内核。 Pigz 由 Mark Adler 编写,使用 zlib 和 pthread 库。要编译和使用 pigz,请阅读源代码分发中的自述文件。您可以在此处阅读pigz手册页面。

手册页 http://zlib.net/pigz/pigz.pdf,其中包含有用的信息。

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

每个部分原始放气流都由一个空的存储块终止......为了在字节边界处结束该部分位流。

尽管如此,DEFLATE 格式对并行解压缩不利:

解压缩不能并行化,至少如果没有为此目的专门准备的放气流。Asaresult,pigz 使用单个线程(主线程(进行解压,但会创建另外三个线程用于读取、写入和检查计算,这在某些情况下可以加快解压缩速度。