写点什么

把成员变量转换成局部变量会更快吗?

用户头像
chengmboy
关注
发布于: 2021 年 01 月 08 日

直接看一段测试

@Benchmark@Warmup(iterations = 2, time = 1)@Measurement(iterations = 2, time = 1)@Fork(value = 1)@State(Scope.Benchmark)@BenchmarkMode(Mode.AverageTime)@OutputTimeUnit(TimeUnit.NANOSECONDS)public class Performance {
int a;

public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(Performance.class.getSimpleName()) .build();
new Runner(opt).run(); }
@Benchmark public void playWithMember() { int c; for (int i = 0; i < 10000000; i++) { c=a+i; }
}
@Benchmark public void playInStack() { int s = a; int c; for (int i = 0; i < 1000000; i++) { c=s+i; } }
}
复制代码

测试结果

Benchmark                   Mode  Cnt  Score   Error  UnitsPerformance.playInStack     avgt    2  0.406          ns/opPerformance.playWithMember  avgt    2  0.403          ns/op
复制代码

可以看到两者几乎没有差别。这是为什么呢?我们都知道,局部变量可以在栈上直接获取,有直接的字节码指令,而成员变量则需要取到对象,再取到字段值,要两条字节码指令,所以应该成员变量会比局部变量慢都多啊,但是事实明显不是这样。

接下来我们逐步分析,我们先看 java 代码编译后的字节码

成员变量:

果然不出我们所料,成员变量的获取需要两条指令,aload_0 和 getfield。

局部变量:

局部变量也不出我们所料,只有一条指令 iload_1。

但是其实机器并不认识字节码,真正执行的是机器码,也就是我们说的汇编,每条字节码都有自己对应的汇编指令,解析执行阶段是按字节码指令逐个解释执行,但是经过 jit 后,会进行优化和编译成本地代码执行。执行原理我在公众号上分享过,有兴趣都可以翻来看看。

既然如此,我们看下真正执行的汇编代码。

成员变量:


rsi 寄存器存放都是对象地址,加上 0xc 刚好是字段 a 的地址。一条 mov 指令,完成了把对象的成员变量的值转移到寄存器 ebx 上。

局部变量:

第一个圈为把成员变量转换为局部变量,第二个是使用局部变量。可以看到使用局部变量也是一条 mov 指令就可以了。

到这里我们发现,使用局部变量和成员变量,都是一条 mov 指令,所以性能是一样的。这也跟我们前面的测试结果相吻合。但是细心的同学会发现,前面的局部变量的基准测试反而慢了一点点,原因是局部变量多了一句成员变量到局部变量到转换,但是真正在使用效率上是一样的。

其实我们刚才看的是 c1 编译后的代码,如果是 c2 完全优化后,它们是一摸一样。

成员变量:

局部变量:

可以发现它们最终的执行是个空方法,因为经过逃逸分析,无用代码消除,我们里面的变量并没有实质的作用。

到这里是不是真相了呢?不是,如果我们让代码不进行编译,完全解释执行看看。

在基准测试类上加上 @CompilerControl(CompilerControl.Mode.EXCLUDE)


Benchmark Mode Cnt Score Error UnitsPerformance.playInStack avgt 2 11204872.244 ns/opPerformance.playWithMember avgt 2 102200762.550 ns/op
复制代码

可以看到,这里就符合我们最初的预期。成员变量比局部变量慢很多,差不多一个数量级。并且解释执行比 jit 后慢很多。


总结,局部变量在基于栈,解释执行的时候有优势,但是在编译成本地代码,都是基于寄存器来执行运算,它们性能就差不多了,甚至经过 jit 编译技术优化后,可能完全相同。


发布于: 2021 年 01 月 08 日阅读数: 19
用户头像

chengmboy

关注

还未添加个人签名 2019.02.21 加入

还未添加个人简介

评论

发布
暂无评论
把成员变量转换成局部变量会更快吗?