2017-04-18 103 views
-1

如何使用python以与底层库相媲美的速度使用gzip/gunzip文件?Python中更快,更好的gunzip(和一般文件输入/输出)

tl;dr - Use shutil.copyfileobj(f_in, f_out). 

我解压缩* .gz文件解作为一个更大的一系列文件处理的一部分,和分析,试图让蟒蛇进行“接近”内置的脚本。由于我正在使用的数据量很大,这很重要,而且这似乎是一个很重要的理解。

使用在500MB〜在 'gunzip解' bash命令如下收益:

$time gunzip data.gz -k 

real 0m24.805s 

一个天真的Python实现是这样的:

with open('data','wb') as out: 
    with gzip.open('data.gz','rb') as fin: 
     s = fin.read() 
     out.write(s) 

real 2m11.468s 

不读取整个文件到内存:

with open('data','wb') as out: 
    with gzip.open('data.gz','rb') as fin: 
     out.write(fin.read()) 

real 1m35.285s 

检查本地计算机缓冲区大小:

>>> import io 
>>> print io.DEFAULT_BUFFER_SIZE 
8192 

使用缓冲:

with open('data','wb', 8192) as out: 
    with gzip.open('data.gz','rb', 8192) as fin: 
     out.write(fin.read()) 

real 1m19.965s 

使用尽可能多的缓冲地:

with open('data','wb',1024*1024*1024) as out: 
    with gzip.open('data.gz','rb', 1024*1024*1024) as fin: 
     out.write(fin.read()) 

real 0m50.427s 

所以很明显它是缓冲/ IO约束。

我有一个适度复杂的版本,运行在36秒,但涉及预先分配的缓冲区和严密的内部循环。我希望有一个“更好的方法”。

上面的代码是合理和清晰的,尽管仍比bash脚本慢。但是,如果有一个非常迂回或复杂的解决方案,它不适合我的需求。我主要的警告是我想看到一个“pythonic”的答案。

当然,总是有这种解决方案:

subprocess.call(["gunzip","-k", "data.gz"]) 

real 0m24.332s 

但对于这个问题的目的,是有处理文件“pythonically”的一个更快的方法。

+0

有时python并不总是答案,那有什么问题? –

+4

你的例子确实没有任何意义:python示例的所有三个例子1)只是复制并且根本不解压2)一次将文件读入内存3)不受io缓冲的限制。此外,'gunzip'和cpython的'gzip'模块使用完全相同的底层库 – user2722968

+0

感谢您的支持。为必要的编辑道歉。我提前提交了提交。 1)错过了我的工作代码中的gzip前缀。现在添加。 2/3)通过缓冲它提高了2倍的速度。 4)是的,它确实使用底层库,所以我试图理解它为什么这么慢,特别是考虑到子进程版本和底层库一样快。 4b)foo.gz是来自样本的剪切和粘贴,现在已经修复。 5.)8219是一个错字。应该是8192,这对应于系统的缓冲区大小并增加了小的速度增加。 – JHiant

回答

0

我打算发表我自己的回答。事实证明,您确实需要使用中间缓冲区; python不能很好地处理这个问题。您需要充分利用该缓冲区的大小,并且“默认缓冲区大小”确实可以获得最佳解决方案。在我的情况下,一个非常大的缓冲区(1GB)和比默认值(1KB)小的缓慢。

此外,我尝试使用它们的readinto()选项构建io.BufferedReader和io.BufferedWriter类,并发现这不是必需的。 (并非完全如此,因为gzip的图书馆是一个BufferedReader如此规定这一点。)

import gzip 

buf = bytearray(8192) 
with open('data', 'wb') as fout: 
    with gzip.open('data.gz', 'rb') as fin: 
     while fin.readinto(buf): 
      fout.write(buf) 

real 0m27.961s 

虽然我怀疑这是一个已知的蟒纹,似乎有很多人通过这种困惑,所以我将离开这个希望它能帮助别人。

@StefanPochmann得到了正确答案。我希望他发布,我会接受。解决方法是:

import gzip 
import shutil 
with open('data', 'wb') as fout: 
    with gzip.open('data.gz', 'rb') as fin: 
     shutil.copyfileobj(fin,fout) 

real 0m26.126s 
+1

如[gzip'示例](https://docs.python.org/3.6/library/gzip.html#examples-of-usage)中的建议,如何使用'shutil.copyfileobj'? (当然除了压缩而不是压缩) –

+0

谢谢@StefanPochmann。是的,这是最好的解决方案。进来26秒足够接近原生解决方案。干杯。 – JHiant

+0

我不打算发表一个答案,部分原因是我不能做你的时间。随意接受你自己的一旦可能。 –