JHi*_*ant 3 python io optimization gzip gunzip
你如何以与底层库相当的速度使用 python gzip/gunzip 文件?
tl;dr - Use shutil.copyfileobj(f_in, f_out).
Run Code Online (Sandbox Code Playgroud)
我正在将 *.gz 文件解压缩为更大系列文件处理的一部分,并进行分析以尝试让 python 对内置脚本执行“关闭”。考虑到我正在处理的数据量,这很重要,而且理解起来似乎是一件很重要的事情。
在 ~500MB 上使用 'gunzip' bash 命令产生如下结果:
$time gunzip data.gz -k
real 0m24.805s
Run Code Online (Sandbox Code Playgroud)
一个简单的 python 实现看起来像:
with open('data','wb') as out:
with gzip.open('data.gz','rb') as fin:
s = fin.read()
out.write(s)
real 2m11.468s
Run Code Online (Sandbox Code Playgroud)
不要将整个文件读入内存:
with open('data','wb') as out:
with gzip.open('data.gz','rb') as fin:
out.write(fin.read())
real 1m35.285s
Run Code Online (Sandbox Code Playgroud)
检查本地机器缓冲区大小:
>>> import io
>>> print io.DEFAULT_BUFFER_SIZE
8192
Run Code Online (Sandbox Code Playgroud)
使用缓冲:
with open('data','wb', 8192) as out:
with gzip.open('data.gz','rb', 8192) as fin:
out.write(fin.read())
real 1m19.965s
Run Code Online (Sandbox Code Playgroud)
使用尽可能多的缓冲:
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
Run Code Online (Sandbox Code Playgroud)
很明显,它是缓冲/IO 限制的。
我有一个中等复杂的版本,运行时间为 36 秒,但涉及预先分配的缓冲区和紧密的内部循环。我希望有一种“更好的方法”。
上面的代码合理且清晰,尽管仍然比 bash 脚本慢。但是如果有一个非常迂回或复杂的解决方案,它就不适合我的需求。我的主要警告是我想看到一个“pythonic”的答案。
当然,总有这样的解决方案:
subprocess.call(["gunzip","-k", "data.gz"])
real 0m24.332s
Run Code Online (Sandbox Code Playgroud)
但是就这个问题而言,是否有一种更快的方式来“pythonically”处理文件。
我要发布我自己的答案。事实证明,您确实需要使用中间缓冲区;python 不能很好地为你处理这个问题。您确实需要调整该缓冲区的大小,并且“默认缓冲区大小”确实获得了最佳解决方案。就我而言,非常大的缓冲区 (1GB) 和小于默认值 (1KB) 的缓冲区较慢。
此外,我尝试了内置的 io.BufferedReader 和 io.BufferedWriter 类及其 readinto() 选项,发现这不是必需的。(不完全正确,因为 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
Run Code Online (Sandbox Code Playgroud)
虽然我怀疑这是一个已知的 python 模式,但似乎有很多人对此感到困惑,所以我将把它放在这里,希望它可以帮助其他人。
@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
Run Code Online (Sandbox Code Playgroud)