2017-04-18 38 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

我不打算髮表一個答案,部分原因是我不能做你的時間。隨意接受你自己的一旦可能。 –