函數嵌套調用性能好仍是函數分開寫性能好?

函數嵌套調用性能好仍是分開寫性能好?
下午忽然想到一個問題: 
* 形式以下的兩種方法,哪種的效率更高一點? 
第一種: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());

    } }

相關文章
相關標籤/搜索