Java 枚举的性能?
我正在实现一个 2 人游戏,该游戏将在紧密循环中运行数十万次,此时性能至关重要。
我的代码实际上看起来像这样:
public class Table {
private final int WHITE_PLAYER = +1;
private final int BLACK_PLAYER = -1;
private final int currentPlayer;
private final int otherPlayer;
...
}
我想知道我是否会选择替换
private final int WHITE_PLAYER = +1;
private final int BLACK_PLAYER = -1;
为定义的枚举,因为
public enum Players {
WhitePlayer,
BlackPlayer
}
我认为枚举只是整数常量的语法糖,并且对字节码进行了简单的观察, 我是否会受到任何性能影响为测试枚举生成的以及调用它的代码似乎表明使用它们确实与进行静态方法调用相同,但对于首次运行时设置的某些枚举基础结构而言。
我的假设是使用枚举作为静态常量确实是相同的,还是正确的,还是我在这里遗漏了一些东西?
I am implementing a 2-player game that will be run in a tight loop literally hundreds of thousands of times, being then performance paramount.
My code actually looks something like this:
public class Table {
private final int WHITE_PLAYER = +1;
private final int BLACK_PLAYER = -1;
private final int currentPlayer;
private final int otherPlayer;
...
}
I was wondering if I would get any performance hit would I choose to replace
private final int WHITE_PLAYER = +1;
private final int BLACK_PLAYER = -1;
to an enum defined as
public enum Players {
WhitePlayer,
BlackPlayer
}
I had the idea that enums were just syntactic sugar over integer constants, and taking a glaze look over the bytecode generated for a test enum, as well as the code calling it, seems to indicate that using them is indeed the same as making a static method call but for some enum infrastructure that is set up when it's first run.
Is my assumption that it is indeed the same to use enums as static constants correct or am I missing something here?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
在微基准测试中,是的,检查整数常量相等性比检查枚举常量相等性更快。
然而,在真实的应用程序中,更不用说游戏了,这完全无关紧要。 AWT 子系统(或任何其他 GUI 工具包)中发生的事情使这些微观性能考虑因素相形见绌许多数量级。
编辑
那么让我详细说明一下。
枚举比较如下:
小整数的整数比较如下:
显然,第一个比第二个需要更多工作,尽管差异很小。
运行下面的测试用例:
你会发现枚举比较比整数比较慢,在我的机器上大约慢了1.5%。
我想说的是,这种差异在实际应用中并不重要(“过早优化是万恶之源”)。我以专业的方式处理性能问题(请参阅我的个人资料),并且我从未见过可以追溯到这样的热点。
In a micro-benchmark, yes, checking integer constant equality will be faster than checking enum constant equality.
However, in a real application, let alone a game, this will be totally irrelevant. The things that are happening in the AWT subsystem (or any other GUI toolkit) dwarf these micro-performance considerations by many orders of magnitude.
EDIT
Let me elaborate a little then.
An enum comparison goes like this:
An integer comparison for a small integer goes like this:
Obviously, the first is more work than the second, although the difference is quite small.
Run the following test case:
and you will find that the enum comparison is slower that the integer comparison, on my machine by around 1.5%.
All I was saying is that this difference will not matter in a real application ("Premature optimization is the root of all evil"). I deal with performance problems on a professional basis (see my profile) and I have never seen a hot spot that could be traced to something like this.
在关心性能之前,您应该关心拥有漂亮且可读的代码。在您的分析结果(无需猜测!)表明枚举是瓶颈之前,请忘记性能并使用更容易理解的内容。
You should care about having nice and readable code before you care about performance. Until your profiling results (no guessing!) show that enums are the bottleneck, forget about performance and use whatever is easier to understand.
JIT 会优化很多事情,使得这样的事情在运行一段时间后变得无关紧要,
更不用说如果你在代码中犯了错误,枚举会更具可读性并且更加万无一失
JIT will optimize a lot of things making things like this irrelevant after it's been running for a while
not to mention enums are more readable and more foolproof should you make an error in your code
我特别关心在 switch 语句中使用枚举。我将使用一个程序来计算一个很长的数组中有限符号集的出现次数。
首先定义一些常量,
以及相应的枚举。
枚举具有由构造函数设置的字段代码。我们将在测试中使用此代码
稍后,这可以产生一些加速。
符号集存储在一个数组中,以及一个对应的 int 数组。
符号在方法中进行计数。
和整数的类似方法。
我们可以使用 Enum 中的代码来提高切换效率。
三种方法各运行 10 次。给出以下时间。
总计 enum 2,548,251,200ns 代码 2,330,238,900ns int 2,043,553,600ns
百分比 enum 100% code 91% int 80%
显着缩短使用整数的时间。使用代码字段可以使计时介于枚举和整数之间。
这些时间上的差异可以很容易地通过周围的代码消失。例如,使用 ArrayList 而不是数组可以使时间差异消失
完全地。
使用
Enum.ordinal()
方法还有另一种选择。这与使用 getCode() 的性能类似。此方法依赖的原因和原因在中讨论使用枚举序数的好习惯?。在我的应用程序中,反向波兰计算器(这个循环和 switch 语句)是程序的核心,运行数百万次,并且出现在性能分析中。
枚举用于操作码:PUSH、POP 等,每个命令都包含一个带有附加参数的操作码。
构建命令列表可以使用枚举,而不需要知道实际的 int 值。
对于代码的非关键部分,我们可以在开关中使用枚举。在Command的toString()方法中说。
但关键部分可以使用代码。
为了额外的性能。
switch 语句的字节码很有趣。在 int 示例中,swicth 语句编译为:
tableswitch 命令根据值在代码中有效地向前跳转。
使用代码(或序数)的开关的代码类似。只需额外调用 getCode() 方法即可。
仅使用枚举代码会更复杂。
这里首先调用一个新方法 $SWITCH_TABLE$toys$EnumTest$Symbol:()
此方法创建一个数组,将序数值转换为开关中使用的索引。基本上它相当于
switch 表创建方法,在第一次调用时计算一次表,并在每次后续调用时使用相同的表。因此,与整数情况相比,我们看到两个非常简单的函数调用和一个额外的数组查找。
I'm particularly concerned with using enums in switch statements. I'll be using a program to count the number of occurrences of a finite set of symbols in a very long array.
First define some constants,
and a corresponding enum.
The enum has a field code set by a constructor. We will use this code in our testing
later, which can yield some speed-up.
The set of symbols is stored in an array, and a corresponding int array.
The symbols are counted in a method.
and similar method for integers.
We can use the code from the Enum to make the switch a little more efficient.
Running the three methods 10 time each. Gives the following timings.
Totals enum 2,548,251,200ns code 2,330,238,900ns int 2,043,553,600ns
Percentages enum 100% code 91% int 80%
Giving a noticeable time improvement for using integers. Using the code field gives timing half-way between enums and ints.
These difference in timing can easily disappear by the surrounding code. For instance of using an ArrayList rather than an array makes the timings difference vanish
completely.
There is another option in using the
Enum.ordinal()
method. This has perfomance similar to using a getCode(). The why and wherfore of this methods depend on are discussed at Is it good practice to use ordinal of enum?.In my application, a reverse polish calculator, this loop and switch statement, is the heart of the program, run millions of times, and it comes up in performance analysis.
There enums are used for opcodes: PUSH, POP, etc. and each command consist of an opcode with additional arguments.
Building the list of commands can use the enum, without needing to know about the actual int values.
For non critical parts of the code we can use the enum in a switch. Say in the toString() method of the Command.
But critical parts can use the code.
for that extra bit of performance.
The byte code of the switch statements are interesting. In the int examples the swicth statment compiles to:
The tableswitch command efficiently jumps forward in the code depending on the value.
The code for the switch using the code (or ordinal) is similar. Just with an extra call to the getCode() method.
Using just the enum the code is more complex.
Here there is first a call to a new method $SWITCH_TABLE$toys$EnumTest$Symbol:()
this method creates an array translating the ordinal values to an index used in the switch. Basically its equivalent to
The switch table creation method, calculates the table once on its first call, and uses the same table on each subsequent call. So we see two quite trivial function calls and one extra array lookup when compared to the integer case.
你的假设是正确的。 Java 确保枚举只有一个实例,因此 == 与比较 int 一样高效。
Your assumption is correct. Java makes sure there is only ever one instance of the enum so that == is as efficient as comparing ints.