函数嵌套调用性能好还是分开写性能好?
下午突然想到一个问题: * 形式如下的两种方法,哪一种的效率更高一点? 第一种: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 版权声明:本文为博主原创文章,转载请附上博文链接!