2010-07-06 88 views
3

我有一個程序,其中每個線程一次從文件中讀取多個文件,處理這些行,並將這些行寫入另一個文件。四個線程拆分文件列表以在其中進行處理。我有奇怪的性能問題,在兩個情況:Java文件I/O吞吐量下降

  • 四個文件與50,000行每
    • 吞吐量開始於處理700行/秒,下降到約100行/秒
  • 30000名的文件與12行,每行
    • 吞吐量最低大約8​​00行/秒和保持穩定

這是內部軟件我工作所以很遺憾我不能共享任何源代碼,但該方案的主要步驟是:在四個工作線程文件

  1. 拆分列表
  2. 啓動所有主題。
  3. 線程一次讀取最多100行,並存儲在String[]數組中。
  4. 線程將轉換應用於數組中的所有行。
  5. 線程將行寫入文件(與輸入文件不同)。
  6. 每個線程重複3-5次,直到所有文件完全處理完畢。

我不明白的是爲什麼每行有12行的30k文件比起每行有很多行的幾個文件給了我更多的性能。我會期望打開和關閉文件的開銷要大於讀取單個文件的開銷。另外,前一種情況的表現下降是指數性的。

我已經設置了最大堆大小爲1024 MB,它似乎最多使用100 MB,所以過載GC不是問題。你還有其他建議嗎?

回答

3

從你的號碼,我想這就是GC可能不是問題。我懷疑這是磁盤的正常行爲,由許多併發線程操作。當文件很大時,磁盤必須多次切換線程之間的上下文(產生大量磁盤seek time),並且開銷很明顯。對於小文件,也許它們被讀取爲單個塊而無需額外的查找時間,因此線程不會彼此干擾太多。

當用單一的標準盤工作時,串行IO通常是更好的是並行IO。

+0

我會嘗試重新編碼它,以便主線程一次讀取多行,允許多個工作線程處理,然後主線程再次將結果寫出。謝謝! – 2010-07-06 21:33:52

1

您是否嘗試過運行Java分析器?這將指出代碼的哪些部分運行速度最慢。從this discussion,它似乎是Netbeans profiler是一個很好的檢查。

+0

我已經看過使用Eclipse的MAT插件的堆轉儲,但它不是特別有用。它在第一個案例中告訴我的是,我有很多'String'被存儲,我知道。我會看看Netbeans的。 – 2010-07-06 21:13:42

+0

我並不真正對存儲在堆上的內容感興趣(立即)。相反,我想知道兩種情況下哪些語句最耗時完成。這至少會告訴你是否存在內存壓力(創建字符串需要永久),或者文件I/O(閱讀需要永久),文件訪問(打開需要永久)還是其他的東西! – Karmastan 2010-07-07 00:41:30

1

可能你的線程持續時間太長,而且緩衝的String []太長。即使你的堆比你需要的大得多,吞吐量也可能因垃圾收集而受到影響。看看你持有這些引用多久。

您可能還會等待虛擬機分配更多內存 - 要求Xmx1024m不會立即分配太多內存,因爲它需要更多內存才能抓取所需內容。您也可以嘗試-Xms1024m -Xmx1024m(即在開始時分配所有內存)以測試是否如此。

+0

我確實啓用了這兩個選項。同一個數組不斷重複使用,除了每次讀取一行時分配新的字符串,所以我假設所有引用被覆蓋,可以立即被GC收集。我應該在寫入時明確地將引用設置爲null嗎? – 2010-07-06 21:20:16

0

您的線程可能會出現停止和鎖定情況(一個線程將100行讀入內存並保存到鎖中,直到完成處理爲止,而不是在完成從文件中讀取時放棄)。我不擅長Java線程,但需要考慮。

+0

嗯,每個線程都有自己的Reader和Writer,並且沒有兩個線程觸及相同的文件。可能仍然存在鎖定問題? – 2010-07-06 21:21:24

+0

我的猜測是,如果線程之間沒有共享,就沒有鎖定問題。我想我喜歡你最好的答案。 – 2010-07-06 21:48:47

2

我假設文件位於同一磁盤上,在這種情況下,你可能顛簸磁盤(或無效的磁盤\ OS緩存)與多個線程試圖同時讀取和寫入並行。一個更好的模式可能是有一個專用的reader/writer線程來處理IO,然後改變你的模式,以便轉換(這聽起來很昂貴)的工作由多個線程處理。當結果變得可用時,您的IO線程可以使用變換操作獲取並覆蓋寫入。這應該停止磁盤抖動,並平衡模式的IO和CPU端。

0

我會檢查這個過程。如果您使用BufferedReader和BufferedWriter,則一次無法讀取和處理100行代碼。這只是增加了複雜性和潛在錯誤的另一個來源。一次做一件,簡化您的生活。