博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
函数嵌套调用性能好还是函数分开写性能好?
阅读量:6938 次
发布时间:2019-06-27

本文共 2835 字,大约阅读时间需要 9 分钟。

hot3.png

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

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

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反编译的中间代码上能看出上面两个问题的答案: 

第一种形式: 

第二种形式: 

从代码上,可以看出,第一种形式的确会多保存两个临时变量,多了成对的两个命令’astore’、’aload’从寄存器中存读变量。 

而第二种形式在编译成中间代码之后,函数的调用顺序也变成了fun1->fun2->fun3,而且没有中间变量。 
后来我又想了想,其实第一种形式和第二种形式函数调用对栈的影响次数是一样的,即便第二种函数切换频繁,在机器层面也是按部就班地做,性能上应当相同。

结论: 

第二种形式在性能上更优

题外话:

在看反编译的中间代码时我发现,即便普通的字符串拼接,到了java字节码的层次,也是通过StringBuilder完成的,也就是说简单的字符串拼接,性能上应当和使用StringBuilder一样,字符串拼接在这里不会形成性能瓶颈,但是也有例外。 

比如,我在下面的测试代码里用的那样,在for循环中循环字符串拼接,这个时候会反复调用StringBuilder的toString方法,导致产生大量的String挤占堆空间,结果我的测试代码就显示OOM了,所以如果要使用以下代码请调节循环次数。
从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());

    }
}

--------------------- 

作者:lqadam 
来源:CSDN 
原文:https://blog.csdn.net/lqadam/article/details/53395019 
版权声明:本文为博主原创文章,转载请附上博文链接!

转载于:https://my.oschina.net/u/4000302/blog/3048080

你可能感兴趣的文章
我的友情链接
查看>>
ibatis动态语句中的prepend
查看>>
keepalived实现LVS的高可用以及实现web服务的高可用(主从模型、双主模型)
查看>>
linux apache
查看>>
Mysql DBA 高级运维学习之路-删除表中数据
查看>>
4.26日第14次作业,23章项目整体绩效评估,24-32章信息安全相关知识
查看>>
新一代java模板引擎典范 Beetl
查看>>
centos6.8+nginx搭建简单的https服务器
查看>>
cut,sort,wc,uniq,tee,tr,split,并且,和,或者
查看>>
LVS负载均衡之三:LVS-DR搭建web群集、LVS结合Keepalived搭建高可用web群集
查看>>
JavaScript 堆内存分析新工具 OneHeap
查看>>
浅谈java异常机制
查看>>
Docker 监控之 SaaS 解决方案
查看>>
用struts2实现简单的登录操作
查看>>
openstack-理解cinder服务
查看>>
基于Karma 和 Jasmine 的Angular 测试(持续更新中)
查看>>
Maven入门指南(一)
查看>>
Silverlight自定义数据绑定控件应该如何处理IEditableObject和IEditableCollectionView对象...
查看>>
在CMD命令行下关闭进程的命令
查看>>
zabbix 4.0安装
查看>>