我试图从多个线程读取一个gzip压缩文件。
我认为这会显着加快解压缩过程,因为多个线程中的gzread
函数从不同的文件偏移量开始(使用gseek
),因此它们会读取文件的不同部分。
简化代码就像
// in threads
auto gf = gzopen("file.gz",xxx);
gzseek(gf,offset);
gzread(xx);
gzclose(gf);
令我惊讶的是,我的多线程版程序确实不加速。 20线程版本使用与单线程版本完全相同的时间。我很确定这远离磁盘瓶颈。
我猜zlib充气功能可能需要解压缩整个文件以便读取一小部分,但我没有从他们的手册中获得任何线索。
任何人都知道如何加快我的速度?
答案 0 :(得分:4)
简短回答:由于deflate流的串行特性,gzseek()
必须解码从启动到请求的搜索点的所有压缩数据。所以你不能通过你想做的事情获得任何好处。实际上,花费的总周期将随着压缩数据长度的平方而增加!所以不要这样做。
答案 1 :(得分:1)
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 /来自谷歌)
pigz,代表gzip的并行实现,是gzip的全功能替代品,在压缩数据时利用多个处理器和多个核心。 pigz由Mark Adler编写,使用zlib和pthread库。要编译和使用pigz,请阅读源代码分发中的README文件。您可以阅读pigz manual page here.
手册页为http://zlib.net/pigz/pigz.pdf,并提供了有用的信息。
它使用与zlib兼容的格式,但采用并行压缩:
每个部分原始deflate流都由一个空的存储块终止...以便在字节边界处结束该部分比特流。
但是,DEFLATE格式对并行解压缩不利:
无法并行化解压缩,至少在没有为此目的专门准备的收缩流的情况下。 Asaresult,pigz使用单个线程(主线程)进行解压缩,但会创建三个其他线程用于读取,写入和检查计算,这可以在某些情况下加速解压缩。
答案 2 :(得分:1)
tl; dr:zlib不是为随机访问而设计的。 It seems possible to implement虽然需要完整的阅读来构建索引,但在您的情况下可能没有帮助。
让我们看一下zlib source。 gzseek是gzseek64的包装,其中包含:
/* 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;
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会将整个文件解压缩到该点。