我寫了一個簡單的循環(huán)來計算一個簡單的操作娇跟,
我要研究一下他為啥這么慢?
public class Benchmark {
public static void main(String[] arg) {
long before = System.currentTimeMillis();
int sum = 0;
for (int index = 0; index < 10*1000*1000; index += 1) {
sum += index;
}
long after = System.currentTimeMillis();
System.out.println("Elapsed time: " +
Long.toString(after - before) +
" milliseconds");
}
}
HotSpot的工作原理:
? ? ? ?它首先使用解釋器運行程序太颤。當(dāng)它發(fā)現(xiàn)某些方法是“熱”時 - 也就是說苞俘,執(zhí)行了很多,要么因為它被調(diào)用很多龄章,要么因為它包含循環(huán)很多的循環(huán) - 它會發(fā)送該方法以進行編譯吃谣。之后,將發(fā)生兩件事之一做裙,或者在下次調(diào)用該方法時岗憋,將調(diào)用編譯版本(而不是解釋版本),或者使用編譯方法替換當(dāng)前長時間運行的循環(huán)锚贱,同時仍在運行澜驮。后者被稱為“堆棧替換”或OSR。
同時惋鸥,如果你堅持使用/編寫這樣的微基準(zhǔn)杂穷,你可以通過將main的主體移動到一個新方法并從main調(diào)用一次來給編譯器編譯代碼的機會悍缠,然后調(diào)用它再次出現(xiàn)在計時器中,看看HotSpot的速度有多快耐量。
另請參閱JavaOne 2002演示文稿?S-1816如何不編寫Microbenchmark
我正在嘗試計算方法調(diào)用時間飞蚓。我不希望有任何額外的工作,所以我使用一個空方法廊蜒。但是當(dāng)我使用HotSpot運行時趴拧,我得到的速度令人難以置信。這是我的代碼:
public class EmptyMethod {
public static void method() {
}
public static void runTest() {
long before;
long after;
// First, figure out the time for an empty loop
before = System.currentTimeMillis();
for (int index = 0; index < 1*1000*1000; index += 1) {
}
after = System.currentTimeMillis();
long loopTime = after - before;
System.out.println("Loop time: " +
Long.toString(loopTime) +
" milliseconds");
// Then time the method call in the loop
before = System.currentTimeMillis();
for (int index = 0; index < 1*1000*1000; index += 1) {
method();
}
after = System.currentTimeMillis();
long methodTime = after - before;
System.out.println("Method time: " +
Long.toString(methodTime) +
" milliseconds");
System.out.println("Method time - Loop time: " +
Long.toString(methodTime - loopTime) +
" milliseconds");
}
public static void main(String[] arg) {
// Warm up the virtual machine, and time it
runTest();
runTest();
runTest();
}
}
空方法不計算在內(nèi)山叮。而且您還看到生成的代碼對齊對象非常敏感著榴。
對空方法的調(diào)用正在被內(nèi)聯(lián),因此實際上沒有時間調(diào)用屁倔。編譯器在其調(diào)用站點將內(nèi)聯(lián)小方法脑又。這減少了對小方法的調(diào)用的開銷。這對于用于提供數(shù)據(jù)抽象的訪問器方法特別有用锐借。如果該方法實際為空问麸,則內(nèi)聯(lián)將完全刪除該調(diào)用。
代碼生成到內(nèi)存中并從那里執(zhí)行钞翔。代碼在內(nèi)存中的布局方式對其執(zhí)行方式有很大影響严卖。在我的機器上的這個例子中,聲稱調(diào)用該方法的循環(huán)更好地對齊布轿,因此比試圖計算運行空循環(huán)所需多長時間的循環(huán)運行得更快哮笆,所以我得到負數(shù)?methodTime-loopTime。
好的汰扭,所以我會在方法的主體中放入一些隨機代碼稠肘,因此它不是空的,并且內(nèi)聯(lián)不能只刪除它东且。這是我的新方法(并且調(diào)用站點更改為調(diào)用方法(17)):
public static void method(int arg){
int value = arg + 25;
}
HotSpot編譯器足夠智能启具,不會為死變量生成代碼。
在上面的方法中珊泳,從不使用局部變量鲁冯,因此沒有理由計算其值。因此色查,方法體再次為空薯演,當(dāng)代碼被編譯時(并且內(nèi)聯(lián),因為我們刪除了足夠小的代碼以便內(nèi)聯(lián))秧了,它又變成了一個空方法跨扮。
對于那些不習(xí)慣處理優(yōu)化編譯器的人來說,這可能是令人驚訝的,因為他們可以非常聰明地發(fā)現(xiàn)和消除死代碼衡创。它們偶爾可能相當(dāng)愚蠢帝嗡,所以不要指望編譯器對代碼進行任意優(yōu)化。
死代碼消除也擴展到控制流程璃氢。如果編譯器可以在測試中看到特定的“變量”實際上是常量哟玷,則可以選擇不編譯永遠不會執(zhí)行的分支的代碼。這使得微基準(zhǔn)測試“足夠棘手”以實際計算您認為您的計時時間變得棘手一也。
死代碼消除在實際代碼中非常有用巢寡。并不是說人們故意寫死代碼;?但是,由于內(nèi)聯(lián)常常(例如椰苟,方法的實際參數(shù))替換變量抑月,使得某些控制流失效,編譯器通常會發(fā)現(xiàn)死代碼舆蝴。
我正在嘗試對對象分配和垃圾收集進行基準(zhǔn)測試谦絮。所以我有上面的那個,但方法的主體是:
public static void method(){
Object o = new Object();
}
這是HotSpot存儲管理器的最佳案例须误。你會得到不切實際的數(shù)字挨稿。
您正在分配不需要初始化的對象仇轻,并立即將它們放在地板上京痢。(不,編譯器不夠聰明篷店,無法優(yōu)化分配祭椰。)真正的程序確實分配了相當(dāng)數(shù)量的短期臨時對象,但它們也比這個簡單的測試程序更長時間地保留了一些對象疲陕。HotSpot存儲管理器為保留更長時間的對象執(zhí)行更多工作方淤,因此請注意嘗試將此類測試中的數(shù)字擴展到實際系統(tǒng)。
我有一個圖形密集型或基于GUI的程序蹄殃。為什么HotSpot不能讓我的圖形代碼變得更快携茂?
圖形程序?qū)⒋罅繒r間花在本機庫中。
Java應(yīng)用程序的整體性能取決于四個因素:
應(yīng)用程序的設(shè)計
虛擬機執(zhí)行Java字節(jié)碼的速度
執(zhí)行基本功能任務(wù)的庫執(zhí)行的速度(以本機代碼表示)
底層硬件和操作系統(tǒng)的速度
虛擬機負責(zé)字節(jié)代碼執(zhí)行诅岩,存儲分配讳苦,線程同步等。與虛擬機一起運行的是本機代碼庫吩谦,它們通過操作系統(tǒng)處理輸入和輸出鸳谜,尤其是通過窗口系統(tǒng)的圖形操作。在這些本機代碼庫中花費大量時間的程序?qū)⒉粫吹剿鼈冊贖otSpot上的性能提高與花費大部分時間執(zhí)行字節(jié)代碼的程序一樣多式廷。
關(guān)于本機代碼的這種觀察適用于您恰好與應(yīng)用程序一起使用的其他本??機庫或任何本機代碼庫咐扭。
您對HotSpot或任何虛擬機進行基準(zhǔn)測試的建議
這里最好的答案是使用真實的應(yīng)用程序進行基準(zhǔn)測試,因為它們是唯一能夠產(chǎn)生真正差異的應(yīng)用程序。如果無法做到這一點蝗肪,請使用標(biāo)準(zhǔn)SPEC基準(zhǔn)測試袜爪,然后使用其他備受推崇的行業(yè)基準(zhǔn)測試。應(yīng)避免使用微量標(biāo)記薛闪,或至少要謹慎使用饿敲。微基準(zhǔn)測試由于優(yōu)化效果而給出誤導(dǎo)性答案是很常見的。
---------------------
作者:a_Ygygs_Dxdsr_XdMss
來源:CSDN
原文:https://blog.csdn.net/weixin_42749765/article/details/87454013
版權(quán)聲明:本文為博主原創(chuàng)文章逛绵,轉(zhuǎn)載請附上博文鏈接怀各!