在Java字节代码中编写方法时局部变量与操作数堆栈大小的成本

时间:2013-12-06 16:32:44

标签: java bytecode

我目前正在研究一种直接生成Java字节码的工具。在考虑性能时,我可以例如翻译以下Java等价物

int[] val = new int[1];
val[0] = 1 + 1;

以两种不同的方式。在不应用优化的情况下,我可以将声明的代码转换为以下等效的字节代码

ICONST_1
ANEWARRAY I
DUP
ICONST_1
ICONST_1
IADD
AASTORE

其中数组最终是操作数堆栈上的唯一值。此转换需要操作数堆栈大小为4,因为堆栈上最多有四个值(int[], int[], int, int),但此解决方案不会消耗任何局部变量槽。

但我也可以像这样翻译代码片段:

ICONST_1
ICONST_1
IADD
ISTORE_1
ICONST_1
ANEWARRAY I
DUP
ILOAD_1
AASTORE

这种转换会将操作数堆栈的大小要求减少一个槽,但是它会花费一个局部变量槽来存储中间结果。

没有考虑方法中这种插槽和局部变量的可重用性:我应该如何考虑操作数堆栈与局部变量数组的成本?操作数堆栈大小更便宜因为它不是随机存取存储器吗?

感谢您的帮助!

2 个答案:

答案 0 :(得分:5)

这个问题没有一般性的答案。

在解释器模式中,局部变量和堆栈深度在性能方面可能是可以互换的,但它当然是在解释器实现方面。

在JIT模式下,它主要取决于目标架构。如果目标CPU使用寄存器文件编程模型(比如x64 / 86或PPC),那么在生成的机器代码中可能根本就没有任何操作数堆栈 - 它将被转换为寄存器映射(与本地变量竞争)相同的寄存器组)。如果它是面向堆栈的体系结构(Sparc),那么操作数堆栈应该非常快 - 毕竟,它是围绕堆栈构建的。

如果您查看特定字节代码序列的JIT代码,您将只能获得明确的答案。并且代码可能随每个VM版本而变化。这可能是浪费时间来担心以这种方式优化字节代码。

使用javac使用的相同习语使你的字节代码出来。这样,JIT就有机会识别成语,并使用为javac成语手工制作的特殊代码路径对其进行优化。

答案 1 :(得分:2)

简短的回答是你没有。

Hotspot JVM使用及时编译器,因此如果您的代码很简单并且执行很多,它将在运行时进行优化和编译。因此,像这样的微不足道的东西不太重要,如果它确实很重要,它可能不会像你期望的那样。

您最好的办法是弄清楚您是否确实遇到了性能问题,如果有,请尝试进行一些分析。

P.S。 long和double也会在操作数堆栈上占用两个插槽。