繼續最優化代碼
在上面的剖析輸出中
關於這一點
修改後的main()如下:
public static void main (String[] argv)
throws IOException {
Letters letters = new Letters();
long startTime = System
for (int i=
修改後進行測試
● 平台 A: 時間在
● 平台B: 時間在
我們已經大大改善了性能
附加的剖析顯示
CPU SAMPLES BEGIN (total =
rank self accum count trace method
這和我們前面所看到的明顯不同
JDK
為了把MappedByteBuffer對象連接到文件
使用 FileChannel 和 MappedByteBuffer修改代碼
import java
void countCharacters (String filename)
throws IOException {
FileInputStream fis = new FileInputStream(filename);
FileChannel fc = fis
// Get the file
int sz = (int)fc
MappedByteBuffer bb = fc
for (int i=
++countArray[pos];
}
}
fc.close();
}
...
修改後產生如下結果:
● 平台 A: 時間在2.1 和 4.7 秒之間
● 平台B: 時間在11 和13.8 秒之間
這個結果較前一例子有了一些改善。tw.wIngwiT.COM然而,從統計或者可感知角度來說,這可能並不重要。
好了,另一個問題:我們應該如何達到我們的性能目標呢?這些文檔能不能在10秒左右的時間內處理完畢?我們已經離這一目標很近了。我們將再嘗試進行另一最優化。
我們來看一下 FileChannel.map()文檔,描述的底端附近有一引人注意的引用。
對多數操作系統而言,將一文件映射到內存中比通過常用的讀寫方法來讀寫好多字節數據代價更高。從性能角度來說,將相對較大的文件映射到內存中才是值得的。
ByteBuffer 類,是MappedByteBuffer's 超類, 提供了java.nio 包中的可緩沖輸入。我們試著使用ByteBuffer而不是MappedByteBuffer 來讀取數據看看會發生什麼事情。
ByteBuffer類的兩種方法可以獲得ByteBuffer: 方法allocate(int)和 allocateDirect(int)。這兩種方法的關鍵就是緩沖區的大小。 allocateDirect() 產生一個以字節為單位的直接型緩沖區,它能盡可能的執行本地文件的I/O。 allocate()產生一個以字節為單位的非直接型緩沖區,它交換少量的本地代碼用於具有決定性的行為(很可能會慢一些)。Sun推薦將以字節為單位的直接型緩沖區用於與大型文件相關並具有較長生命周期的緩沖區。
我們修改後的代碼使用以字節為單位的直接型緩沖區:
...import java.nio.ByteBuffer;
...
void countCharacters (String filename)
throws IOException {
FileInputStream fis = new FileInputStream(filename);
FileChannel fc = fis.getChannel();
// Get the file's size and then map it into memory
int sz = (int)fc.size();
int bufferSize = 1024;
ByteBuffer bb = ByteBuffer.allocateDirect (bufferSize);
int nbytes = -1;
while ((nbytes = fc.read (bb)) != -1) {
bb.rewind();
for (int i=0; i= 0) && (pos <= 25)) {
++countArray[pos];
}
}
}
fc.close();
}
...
這一代碼運行時間結果:
● 平台 A: 時間在2.7 和 5.2 秒之間
● 平台B: 時間在13.1 和13.9 秒之間
使用 allocateDirect()替換allocate()將以20%的比例降低性能。性能比前一例子會少慢一些。代碼也不是很容易閱讀。可是,很有意思的一點,我在一個具有大約20個Java源文件的目錄上使用相同的代碼進行試驗,結果得到了相反的結果。使用直接ByteBuffer編寫的代碼其速度要比使用MappedByteBuffer編寫的代碼快將近20%。這很可能與緩沖區和同步本地文件I/O操作所產生的附加花費有關。
所以…那一種最優化策略最好呢?這取決於應用是如何加以使用的。根據本文開始列出的需求,我將選擇MappedByteBuffer版本。在我們的測試用例中它是最快的一個而且可以很好進行平衡。
重要的問題是:在我們進行最後一輪最優化測試之前,性能是否足夠好了?如果已經足夠好了,我們只是在花費額外時間進行並無直接好處的優化。如果性能仍然不令人滿意,接下來的優化嘗試很可能會更困難而且也十分耗時。
剖析如此重要的原因就是它告訴您要解決問題的信息,而且同時告訴您是否已經做到了這一點。有時候,花時間修改代碼對性能並沒有顯著的影響。實際上或許僅僅使您的代碼更難於閱讀和維護。而且,您可能會對HotSpot VM 的優化策略感到意外-您不知道如何使Java程序運行的更快。
相同的改善流程對內存相關和I/O相關的性能問題一樣有效。內存相關-內存漏洞和碎片帳集通常是最難追蹤的。這是因為它們難於復制e而且觀測程序或許會改變內存相關的行為。
改善性能的其他方法
一旦剖析確定了性能問題的位置,決定做什麼仍然是需要小心處理的。這裡的一些建議可用於解決特定的性能問題:
● 方法調用:優化方法,或者盡量少調用;
● 類讀取:預先讀取類,使用懶散的(命令觸發)實例。注意:多線程程序中,避免使用兩次檢查的鎖定(請參見Brian Goetz's "Double-Checked Locking: Clever, but Broken" (JavaWorld, February 2001)查明原因);
● 在內存外運行: 共享對象而不是創建對象,可以使用對象工廠或者對象池;
● 字符串處理: 使用 StringBuffer 或者 char[]而不是String;
● 遞歸:消除遞歸方法調用並將其轉換成迭代體
● I/O 和序列: 確保I/O是使用緩沖方法的。考慮使用非同步方法編寫您自己的I/O子類;
● Collection類:使用滿足應用的collection類,有時數組的訪問和修改比collection更快。
性能改善的另一方法:使用字節型代碼優化器。這些程序檢查類文件字節代碼,刪除未被使用的代碼,清空方法調用堆棧等。優化器最好的一點就是您不必修改任何代碼-您只需測試一下看看優化器是否正常工作。令人不是很滿意的地方就是優化器產生不可移植的代碼或代碼並未按照您所期望的方式工作。
調整程序
可感知性能比實際性能更重要。在您開始調整之前,決定如何判定什麼時候性能就是足夠好了。需要調整時,測試並剖析。如果它們還是有區別的,新的1.4 I/O 類可提供性能改善功能。剖析、剖析、再剖析。高效使用您的時間。不要更改代碼而且在獲得滿意的性能之後就不必再調整。
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19458.html