在Java語言中,所有對整數的操作都是int進行的。 因此,如果我們使用short作為循環索引,則在每次迭代時都將進行類型轉換,這實際上比對int的簡單影響要重。
我編寫該代碼來實現我的目標:
package com.wicht.old;public class TestShortInt {public static void main(String[] args){long startTime = System.nanoTime();int resultInt = 0;for (int i = 0; i < 100000; i++){for (int j = 0; j < 32760; j++){resultInt += i * j;}}System.out.println("Temp pour int : " + (System.nanoTime() - startTime) / 1000000 + " ms");startTime = System.nanoTime();int resultShort = 0;for (int k = 0; k < 100000; k++){for (short f = 0; f < 32760; f++){resultShort += k * f;}}System.out.println("Temp pour short : " + (System.nanoTime() - startTime) / 1000000 + " ms");System.out.println(resultInt);System.out.println(resultShort);}
}
結果我發現short比int慢了兩倍,直到一周前我才確信這些結果。
這時,一位讀者(Jean)批評了我的測試結果,并給了我有關微基準測試的幾篇文章的鏈接。 我閱讀了這些文章,并了解了為什么我的結果不正確。
實際上,我的測試沒有注意可能改變測試結果的幾件事:
- JVM預熱 :由于有幾個參數,代碼通常通常會很慢,并且隨著執行時間的增長直到達到穩態,代碼會變得越來越快。
- 類加載 :第一次啟動基準測試時,必須加載所有使用的類,從而增加了執行時間。
- 即時編譯器 :當JVM識別出代碼的重要部分時
- 垃圾收集器 :在基準測試期間可能會發生垃圾收集,并且時間會大大增加。
由于所有這些因素,第一次運行(可能需要運行10秒)比其他運行速度慢,并且會使基準完全錯誤。
那么,我們如何才能取得良好的基準測試結果呢?
這確實很困難,但是我們可以使用Elliptic Group的軟件開發人員Brent Boyer引入的基準框架獲得幫助。 該框架照顧了所有先前引入的因素,并制定了良好的基準。
該框架的使用非常簡單,您只需創建Benchmark類的新實例,并將其傳遞給Callable或Runnable即可,然后直接啟動測試。 這是在循環索引中測試short和int的示例:
public class ShortIndexesLoop {public static void main(String[] args) {Callable callableInt = new Callable(){public Long call() throws Exception {long result = 0;for (int f = 0; f < 32760; f++){result += 444;}return result;}};Callable callableShort = new Callable(){public Long call() throws Exception {long result = 0;for (short f = 0; f < 32760; f++){result += 444;}return result;}};try {Benchmark intBenchmark = new Benchmark(callableInt);System.out.println("Result with int ");System.out.println(intBenchmark.toString());Benchmark shortBenchmark = new Benchmark(callableShort);System.out.println("Result short ");System.out.println(shortBenchmark.toString());} catch (Exception e) {e.printStackTrace();}}
}
要獲得結果,可以使用Benchmark.toString()或Benchmark.toStringFull()獲得更多統計信息。 您還可以使用Benchmark.getSd()直接訪問某些統計信息(例如標準差),也可以直接使用Benchmark.getStats()獲取所有統計信息。
這是前面代碼的結果:
結果int first = 807.056 us,平均值= 46.032 us(CI delta:-261.393 ns,+408.932 ns),sd = 230.929 us(CI delta:-68.201 us,+105.262 us)
結果短短優先= 721.912 us,平均值= 48.234 us(CI delta:-198.625 ns,+254.774 ns),sd = 160.196 us(CI delta:-32.764 us,+37.882 us)
如您所見,短版本僅比int慢104.78%。 這表明最初的結果是完全錯誤的。
這是int版本的完整結果:
動作統計信息:第一個= 807.056 us,平均值= 46.032 us(CI delta:-261.393 ns,+408.932 ns),sd = 230.929 us(CI delta:-68.201 us,+105.262 us)警告:執行時間有極端異常情況,標清值可能不準確---根據塊統計信息計算操作統計信息-每個塊測量32768個任務執行-用戶說任務內部執行m = 1個動作-那么每個塊測量的動作數為a = 32768-阻止統計信息:平均值= 1.508 s(CI增量:-8.565毫秒,+ 13.400毫秒),sd = 41.803毫秒(CI增量:-12.346毫秒,+ 19.054毫秒)–用于將阻止統計信息轉換為行動統計信息的論壇(均值)假設a / a為1 / s,則sd縮放為1 / sqrt(a))假定操作執行時間為iid — —每個置信區間(CI)被報告為點估計的+-增量或封閉點間隔([x,y])–每個置信區間的置信度為0.95 — —-–執行時間顯示在外邊的情況–使用箱線圖確定 中位數= 1.498 s,interquantileRange = 34.127 ms的算法– 3是極度(偏高):#57 = 1.621 s,#58 = 1.647 s,#59 = 1.688 s –2是溫和的(偏高): #55 = 1.570 s,#56 = 1.582 s ———-阻止sd值可能無法反映任務的內在變化–猜測:環境噪聲至少解釋了所測量sd的55.89418621876822%———- –動作sd值幾乎完全相同由離群值填充–根據等值離群值模型,它們至少導致了98.95646276911543%的已測量方差–模型數量:a = 32768.0,muB = 1.5083895562166663,sigmaB = 0.04180264914581472,muA = 4.603239612477619E-5,sigmaA = 2.3092919283255957E- 4,tMin = 0.0,muGMin = 2.3016198062388096E-5,sigmaG = 5.754049515597024E-6,cMax1 = 1252,cMax2 = 322,cMax = 322,cOutMin = 322,varOutMin = 0.0017292260645147487,muG(cOutMin)= 2.3034259031465023E-5, U(cOutMin)= 0.002363416110812895
就像您在使用此框架時可能看到的那樣,當您舉例說明您存在極端異常值可能使標準偏差完全錯誤時,它會向您發出一些警告。
您可以在Elliptic Group的網頁上下載此框架。 我發現它非常強大且易于使用,并且每次需要進行基準測試時都會使用它。
總而言之,我還必須說,即使您使用那種框架,如果您沒有測試代碼的正確部分,也可能會導致非常糟糕的基準測試。 這是來自Brent Boyer的兩篇非常有趣的文章:
- 健壯的Java基準測試,第1部分:問題
- 強大的Java基準測試,第2部分:統計信息和解決方案
參考: 如何通過 @Blog(“ Baptiste Wicht”)的 JCG合作伙伴 Baptiste Wicht 編寫正確的基準 。
- 績效焦慮–關于績效不可預測性,度量和基準
- 改善Java應用程序性能的快速技巧
- 如何在Java中獲得類似于C的性能
- Java中的低GC:使用原語而不是包裝器
- 如何在不到1ms的延遲內完成100K TPS
翻譯自: https://www.javacodegeeks.com/2011/09/java-micro-benchmarking-how-to-write.html