函數嵌套調用性能好仍是分開寫性能好?
下午忽然想到一個問題:
* 形式以下的兩種方法,哪種的效率更高一點?
第一種:java
A=fun1(args1[]);
B=fun2(A,args2[]);
C.fun3(B);
1
2
3
第二種:函數
C.fun3(fun2(fun1(args1[]),args2[]));
1
也就是說,一段結果相同的代碼,是將中間使用的函數嵌套起來寫性能更好仍是分開寫性能更高呢?
這裏假定變量不會再被後面的代碼使用,不存在複用的問題,同時,也將可讀性問題暫放一邊,僅僅從性能角度考慮。性能
直覺上,應當是第二種形式的性能更高一些,減小了中間變量存儲,可是我仍是有一點迷惑:函數調用時系統要保存現場,對各類變量在棧中進行保存,調用完了還要恢復現場,恢復各類上一個方法中的值,第二種形式是否是在這方面消耗了更多性能呢?畢竟在第二種形式中,剛想調用fun3,發現還要調用fun2,剛想調用fun2,發現還必須先調用fun1。測試
本着先敲一敲看看的想法,我用java寫了一個測試代碼(放到文章最後面),從javap反編譯的中間代碼上能看出上面兩個問題的答案:
第一種形式: ui
第二種形式: this
從代碼上,能夠看出,第一種形式的確會多保存兩個臨時變量,多了成對的兩個命令’astore’、’aload’從寄存器中存讀變量。
而第二種形式在編譯成中間代碼以後,函數的調用順序也變成了fun1->fun2->fun3,並且沒有中間變量。
後來我又想了想,其實第一種形式和第二種形式函數調用對棧的影響次數是同樣的,即使第二種函數切換頻繁,在機器層面也是循序漸進地作,性能上應當相同。.net
結論:
第二種形式在性能上更優blog
題外話:字符串
在看反編譯的中間代碼時我發現,即使普通的字符串拼接,到了java字節碼的層次,也是經過StringBuilder完成的,也就是說簡單的字符串拼接,性能上應當和使用StringBuilder同樣,字符串拼接在這裏不會造成性能瓶頸,可是也有例外。
好比,我在下面的測試代碼裏用的那樣,在for循環中循環字符串拼接,這個時候會反覆調用StringBuilder的toString方法,致使產生大量的String擠佔堆空間,結果個人測試代碼就顯示OOM了,因此若是要使用如下代碼請調節循環次數。
從1中能夠看出,其實我測試代碼有問題,原本想找個耗時的操做區分兩種形式代碼的性能,因此使用了String的拼接,結果循環次數一多就OOM,也就是說我沒從實踐中真實測得兩種形式代碼的差異(即使循環次數少的狀況下,屢次測量結果也是千奇百怪,我懷疑係統對程序的調度影響了耗時)
如下我寫的測試代碼:get
public class AnyIdeaTestFirst{
/**
* 靜態內部類,用於返回一個記錄方法耗時和字符串的集合
*/
static class Iner{
String s;
long time;
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
}
public static Iner fun1(String a){
long timeBefore=System.currentTimeMillis();
for(int i=0;i<50;i++){
a+=a;
}
Iner iner=new Iner();
iner.setS(a);
long timeAfter=System.currentTimeMillis();
iner.setTime(timeAfter);
System.out.println("fun1 after:"+(timeAfter-timeBefore));
return iner;
}
public static Iner fun2(Iner iner,String a){
long timeBefore=System.currentTimeMillis();
System.out.println("進入fun2,與退出fun1之間的時間差:"+(timeBefore-iner.getTime()));
for(int i=0;i<30;i++){
iner.setS(a);;
}
long timeAfter=System.currentTimeMillis();
iner.setTime(timeAfter);
System.out.println("fun1 after:"+(timeAfter-timeBefore));
return iner;
}
public static void fun3(Iner iner){
long timeBefore=System.currentTimeMillis();
System.out.println("進入fun3,與退出fun2之間的時間差:"+(timeBefore-iner.getTime()));
}
public static void main(String []args){
System.gc();
System.out.println("方法一開始-------------------------------"+System.currentTimeMillis());
Iner iner1=fun1("a");
Iner iner2=fun2(iner1,"b");
fun3(iner2);
System.out.println("方法一結束--------------------------------"+System.currentTimeMillis());
System.out.println("方法二開始--------------------------------"+System.currentTimeMillis());
fun3(fun2(fun1("c"),"d"));
System.out.println("方法二結束--------------------------------"+System.currentTimeMillis());
} }