2011-04-27 86 views
0

我有一個java程序使用以下邏輯:Java程序的性能問題

i) Open a socket server and wait for messages from client 

ii) Read messages received(fixed length records of about 233 bytes). 

iii) on each message receive, call a process function. 

iv) process function does follwing 
- add the record to the string builder. 

- if (length of stringbuilder > configured buffer size) { 

    process this buffer 

    } else { 

     add the new record to buffer 

    } 

現在當我嘗試這個節目有100萬條記錄,每233個字節,大約需要5分30秒結束。我想減少這個時間。大量的時間在這個過程函數中被浪費了。

我正在檢查是否可以得到一些關於如何重新組織這個過程()以獲得更好的性能的建議。 我的用例是獲取記錄並讀取它們,直到達到配置的緩衝區大小(如50 Mb或500 MB或1 GB)。一旦達到此大小,處理它並寫入文件系統。

+1

@ user656189如果時間花在流程函數上而不是數據傳輸上,我們需要看到的是您在流程函數中所做的事情,您不覺得嗎? – 2011-04-27 23:09:47

+0

您是否考慮過在您的IDE中使用某種形式的性能測試插件來查看瓶頸位置? – Endophage 2011-04-27 23:10:15

+0

@Endophage - cud你建議一些選擇嗎? – user656189 2011-04-28 20:41:04

回答

0

你的系統上有多少核心?您可以創建一個具有相當於number of cores on your system的多個線程的線程池,並創建一個可在線程內調用進程的可運行線程。這可能會加快你的速度。

如果您只有一個核心,我們將需要更多關於處理的信息。你的CPU掛在你的測試中了嗎?什麼操作系統等?

0

一對夫婦的 「微的優化」 的:

  1. 創建正確的初始大小(例如1GB)您的StringBuilder。
  2. 不要每次重新創建StringBuilder;通過設置setLength重用它(0)

但我不確定這樣的微觀優化會有多大的影響。也許你可以發佈更多的代碼?

  • 爲什麼數據存儲在中間緩衝區?如果你所做的只是將它寫入文件系統,那麼使用BufferedWriter一次寫一條記錄會更好嗎?
  • 郵件是否必須按照它們到達的順序進行處理?如果不是,您可以使用ExecutorService來並行處理。
+0

我)我做微觀優化,你指定。我只創建一次stringbuilder並在使用後通過清除它來重新使用它:sb.delete(0,sb.length()); ii)數據存儲在中間緩衝區中,因爲一旦達到它的大小並將其轉換爲Hadoop鍵值格式,然後將這些記錄寫入HadoopFile系統,我正在處理該緩衝區。 iii)你能指點我一些Executor服務的例子嗎? – user656189 2011-04-27 23:24:59

+0

我想到的Executor服務是標準的http://download.oracle.com/javase/6/docs/api/java/util/concurrent/Executors.html。如果您使用的是Hadoop,那麼我明白您需要使用真正大的文件,但我不確定是否需要使用中間緩衝區 - 您可以這樣做:讀取記錄;將轉換後的格式寫入Hadoop;在寫入1GB後關閉當前的Hadoop文件?另外,正如其他人所建議的那樣,重要的是對你的代碼進行分析(如果你還沒有這樣做的話)。沒有意義的是優化錯誤的代碼位... – 2011-04-28 09:28:39

+0

sb。刪除是工作的錯誤工具,它會刪除字符0..length並提出剩下的字符,而你對此沒有真正的興趣。你想要的是sb.setLength(0),它會忽略內容並從頭開始。 – 2013-01-24 17:33:12

0

我懷疑5:30分鐘的大部分時間可以用網絡通信的開銷來表示。我推薦以下內容(最重要的第一項):

  • 如果您使用UDP,請切換到TCP。對於這樣的事情,與基於消息的傳輸相比,基於流的傳輸可能會獲得更好的吞吐量。

  • 在客戶端和服務器端,請確保已經用緩衝流封裝了套接字流。

  • 如果客戶端和服務器位於同一主機上,請使用環回IP地址(例如127.0.0.1)。

  • 如果服務器端處理是CPU密集型的(並且您有多個核心),請在單獨的線程中處理讀取消息的線程。

  • 考慮使用NIO讀取/寫入數據。

  • 考慮不要將數據轉換爲服務器端的字符形式...儘管如果它真的是文本,這會使處理變得困難。


你做的任何之前,瞭解您的客戶端和服務器端應用程序,看看是否揭示了任何意外的瓶頸。

+0

謝謝!這很有幫助。我可以使用哪些工具來分析我的客戶端和服務器端應用程序? – user656189 2011-04-28 20:41:40

+0

嘗試VisualVM - http://download.oracle.com/javase/6/docs/technotes/guides/visualvm/index.html – 2011-04-29 02:03:48

0

信封完整性檢查的一些回去:

  • 您要發送233個* 1M字節= 1.864Gb通過套接字,這是將採取 不同的時間取決於你的帶寬和NIC但 草圖一些基線數字,如果你有一個100Mb卡,你正在尋找 20秒在那裏之前,任何真正的網絡延遲襲擊你。事實上,除非你使用本地主機或硬件和連通性,否則這個數字可能會更高。
  • 將233字節編碼爲一個字符串需要大約260納秒(在我的機器上)使用新的字符串(字節[]),因此您正在查看1M * 0.25us = 250毫秒。

所以關閉蝙蝠字節到字符串是不是問題,我也會猜測StringBuilder.append不是很差(它複製字符數組)。您將爲所有這些字符串和字節緩衝區分配內存,這可能會導致一些緩慢。爲了避免內存流失,你可以使用Charset.newDecoder來獲得一個解碼器(保留和重用),並將ByteBuffer直接寫入一個可重用的CharBuffer中。你提到了一些字符串的格式化,如果做錯了,這可能是昂貴的,但除非你做了一些複雜的事情,否則我不認爲這是問題所在。

更可能的原因是網絡延遲,您可以通過編寫一個只讀取數據並將其丟棄的虛擬程序來測試該理論。 另一個可能的嫌疑犯是寫入文件或磁盤的代碼部分,但沒有更多的信息很難提供幫助。 此外,如果您是新的性能分析,可以使用手挽時間來驗證你的理論:

long start = System.nanotime(); 
process(data); 
long took = System.nanotime() - start; 

總結一下你在過程中花費了多長時間和你有時間去那裏的想法。