2013-12-21 69 views
3

因此,我試圖用Java以zip方式進行只讀訪問,使用多線程方式進行解壓縮,因爲我的ZipFile/ZipEntry標準簡單單線程解決方案使用枚舉和輸入流以及什麼不會導致它花費大約五整秒來解壓縮到內存中一個50兆zip文件,這需要一秒AT MOST讓我的磁盤無需解壓縮即可讀取。Java中的多線程解壓縮

但是,整個Java zip庫同步到令人難以置信的惡劣程度,毫無疑問,因爲它都是抽象的讀/寫/等。在相同的代碼中,而不是具有高效的非同步只讀代碼。

我已經看過第三方Java庫,它們都是比使用大象槍射擊蒼蠅更糟糕的海量VFS庫,否則他們獲得性能優勢的唯一原因是他們多線程無論如何,大部分線程都在磁盤IO上阻塞。

我只想把一個zip文件拉到一個byte []中,分叉一些線程,然後處理它。沒有任何理由需要以任何方式進行任何同步,因爲我在內存中分別使用每個解壓縮文件而沒有交互。

這爲什麼這麼難?

+1

你緩衝自己的輸入和輸出? –

+0

你是什麼意思「拉一個zipfile到一個字節[]」?你的意思是你把它全部讀到一個字節數組中嗎?如果是這樣,它是壓縮格式還是解壓縮格式?以及如何處理文件太大而無法將所有內容保存到字節數組中?我認爲如果你不把它全部放入RAM中,你將回到單個文件被多個線程使用的問題,這通常不會給你任何優於單個線程的優勢。 –

+0

這些文件都不會很大:它們全都適合記憶,空間很小,不會有問題。但使用zip文件的隨機訪問特性會影響性能,而一次讀取整個文件然後在內存中使用它會更好,因爲硬盤驅動器在優化讀取操作方面非常出色。 – JAKJ

回答

1

用Java實現這一點的最快方法是使用NIO。您可以通過使用MappedByteBuffer將文件直接映射到內存中。

FileChannel channel = FileChannel.open(Paths.get("/path/to/zip"), 
    StandardOpenOption.READ); 
MappedByteBuffer buffer = channel.map(MapMode.READ_ONLY, 0, channel.size()); 

現在buffer包含整個文件的內存映射區域。你可以隨心所欲地做任何事情,例如將一個offset和一個length傳遞給一個線程。我不知道哪個zip庫支持,但顯然你已經有這樣的東西。我想測試一下50Mb的單個文件檔案,平均只用不到200ms就可以用一般的ZipInputStream來讀取它 - 我認爲你在這裏幾乎沒有什麼優化。

+0

無論如何,這聽起來像是一個很好的選擇。我會。 – JAKJ

+0

@JAKJ您還可以使用更多的代碼將緩衝區橋接到輸入流,從而將'ZipInputStream'覆蓋在緩衝區上。請參閱:http://stackoverflow.com/a/6603018/540873 –

+0

但這會帶來什麼好處?內存映射文件的內容在被訪問之前仍然在磁盤上,直接訪問磁盤緩存的RAM頁面的低級API與通過常規文件API訪問相同頁面的內容之間的區別是相當微不足道的。包裝成順序的「InputStream」處理隨機訪問文件可能存在的任何優勢。 –

1

只是爲了子孫後代的緣故,在一些測試來回,我終於結束了使用變如下(從關閉的文件從頭一個while (true)循環開始完成迭代)答案:

  • 使用DataInputStream.readFully拉整個(50兆,在這種情況下, )zip文件到byte[]

  • 菌種工作線程(在我的情況下每個物理CPU核心之一,4) 其中每個採取byte[]並創建一個 ZipInputStream(ByteArrayInputStream)。第一個工人跳過0 條目,第二跳跳過1,第二跳跳2等,因此它們都是 彼此偏移1。工作線程完全不同步 ,因此它們都有自己的本地副本,該文件的壓縮文件的元數據爲 ,而不是什麼。這是線程安全的,因爲zip文件是 只讀且工作人員不共享解壓縮數據。

  • 每個工作線程讀取一個條目並對其進行處理,然後跳過 足夠的條目,以使它們再次被偏移1。所以第一個 線程讀取條目0,4,8 ...,第二個讀取1,5,9 ...,然後第 。

  • 所有的工人都用.join()拉回來。

我的時間如下:

  • 讀取壓縮文件到byte[]與所有 (只是IO)沒有解壓給出0.1秒的平均每次迭代。

  • 直接在底層文件使用直的ZipFile爲正常, 產生0.5秒的初始​​尖峯,隨後平均0.26 秒的每次迭代之後(從新鮮開始關閉 先前的ZipFile之後)。

  • 讀取的ZipFile成byte[],沒有多線程 在所有創建 ZipInputStream(ByteArrayInputStream)有了它,結果在0.3秒的初始尖峯,隨後是 平均0.26秒的其後每次迭代,顯示出 磁盤高速緩存具有渲染隨機訪問和 初始讀取等效的效果。

  • 讀取的ZipFile成byte[],產卵4個工作線程 如上所述,與該byte[],並等待他們 光潔度,所帶來的時間回落到平均0.1秒的每 迭代。

因此,判決是,用這種方法我已經成功地將一箇中等規模的壓縮文件的處理與適度強大的計算機下來所花費的時間簡單地物理讀取該文件,如果加上減壓步驟完全不再顯眼。很明顯,對於數以萬計條目的巨大zip文件,這種方法仍然會產生巨大的加速。

似乎我並沒有試圖優化沒有任何東西,考慮到我減少了樣本文件的處理時間(這是我需要處理的最大樣本的大小)到38%的簡單單一線程方法。

考慮如何令人難以置信的這個技巧,工作的工作,想象與實際設計要做到這一點,不帶內置的同步本地Java拉鍊閱讀器類可能加速。