函数嵌套调用性能好还是函数分开写性能好?

函数嵌套调用性能好还是分开写性能好?

下午突然想到一个问题:
* 形式如下的两种方法,哪一种的效率更高一点?
第一种:

A=fun1(args1[]);
B=fun2(A,args2[]);
C.fun3(B);

第二种:

C.fun3(fun2(fun1(args1[]),args2[]));

也就是说,一段结果相同的代码,是将中间使用的函数嵌套起来写性能更好还是分开写性能更高呢?
这里假定变量不会再被后面的代码使用,不存在复用的问题,同时,也将可读性问题暂放一边,仅仅从性能角度考虑。

直觉上,应当是第二种形式的性能更高一些,减少了中间变量存储,但是我还是有一点迷惑:函数调用时系统要保存现场,对各种变量在栈中进行保存,调用完了还要恢复现场,恢复各种上一个方法中的值,第二种形式是不是在这方面消耗了更多性能呢?毕竟在第二种形式中,刚想调用fun3,发现还要调用fun2,刚想调用fun2,发现还必须先调用fun1。

本着先敲一敲看看的想法,我用java写了一个测试代码(放到文章最后面),从javap反编译的中间代码上能看出上面两个问题的答案:
第一种形式:
第一种形式反编译代码
第二种形式:
第二种形式反编译代码
从代码上,可以看出,第一种形式的确会多保存两个临时变量,多了成对的两个命令’astore’、’aload’从寄存器中存读变量。
而第二种形式在编译成中间代码之后,函数的调用顺序也变成了fun1->fun2->fun3,而且没有中间变量。
后来我又想了想,其实第一种形式和第二种形式函数调用对栈的影响次数是一样的,即便第二种函数切换频繁,在机器层面也是按部就班地做,性能上应当相同。

  • 结论:
    第二种形式在性能上更优

  • 题外话:

    1. 在看反编译的中间代码时我发现,即便普通的字符串拼接,到了java字节码的层次,也是通过StringBuilder完成的,也就是说简单的字符串拼接,性能上应当和使用StringBuilder一样,字符串拼接在这里不会形成性能瓶颈,但是也有例外。
      比如,我在下面的测试代码里用的那样,在for循环中循环字符串拼接,这个时候会反复调用StringBuilder的toString方法,导致产生大量的String挤占堆空间,结果我的测试代码就显示OOM了,所以如果要使用以下代码请调节循环次数。
    2. 从1中可以看出,其实我测试代码有问题,本来想找个耗时的操作区分两种形式代码的性能,所以使用了String的拼接,结果循环次数一多就OOM,也就是说我没从实践中真实测得两种形式代码的差别(即便循环次数少的情况下,多次测量结果也是千奇百怪,我怀疑系统对程序的调度影响了耗时)

以下我写的测试代码:

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());


    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值