在Java中,使用“快捷方式”变量会影响性能吗?

3
我有以下代码片段:
Player player = (Player)Main.getInstance().getPlayer();
player.setSpeedModifier(keyMap[GLFW_KEY_LEFT_SHIFT] ? 1.8f : 1);
if (keyMap[GLFW_KEY_W]) {
    player.moveForward();
}
if (keyMap[GLFW_KEY_S]) {
    player.moveBackward();
}
player.rotateTowards(getMousePositionInWorld());

我想知道在编写代码时,为了使代码更易读,使用本地变量(针对玩家)是否会影响性能,或者在编译期间是否会进行优化以替换变量的使用,因为它只是另一个变量的直接复制。虽然可以保留长版本,但我更喜欢简短版本的可读性。我知道如果有任何性能影响,那么影响将微不足道,但我只是想知道是否有任何影响。
谢谢,-Slendy.

我认为这不应该影响性能。一个好的编译器会优化任何形式的代码,如果可以的话,使用寄存器,并且只在需要时将最终值写回全局变量。对于本地变量也是如此。这里的规则是,在测量了性能并确定需要加速之前,不要永远优化代码。 - markspace
我很好奇编译器是否能够可靠地确定 Main.getInstance().getPlayer() 的返回值是否是常量。它可能能够,也可能不能。如果不能,我预计没有“快捷变量”的版本由于函数调用开销而具有(微不足道的)更差的执行时间。 - Zéychin
4个回答

4
对于任何现代编译器而言,这很可能会被优化掉,不会对性能造成任何影响。用于存储的少量额外字节完全值得增加可读性。

3

请考虑以下两段代码:

final Player player = (Player)Main.getInstance().getPlayer();
player.callmethod1();
player.callmethod2();

并且:

((Player)Main.getInstance().getPlayer()).callmethod1();
((Player)Main.getInstance().getPlayer()).callmethod2();

有一些原因,为什么第一个变体更可取:

  1. 首先,第一个变体更易读,至少是因为行长的原因。
  2. Java编译器无法假定 Main.getInstance().getPlayer() 将返回相同的对象,这就是为什么第二个变体实际上会调用两次 getPlayer,这可能会对性能造成影响。

AD 2:JIT 可能会识别和优化它,但是您提到的第一点仍然有效,因此第一个选择更值得推荐。 - glglgl
@glglgl true, JIT 可能在运行时识别它,也可能不会,这取决于许多因素,包括月相,所以我宁愿不依赖于此。 - Iłya Bursov
完全正确。我总是抱怨我的同事让我处理像这样的东西 frobnicate(Controller.getInstance().getFoo().getBar().getBaz1(), Controller.getInstance().getFoo().getBar().getBaz2(), Controller.getInstance().getFoo().getBar().getBaz3()); 而不是一个非常易读的 Bar bar = Controller.getInstance().getFoo().getBar(); frobnicate(bar.getBaz1(), bar.getBaz2(), bar.getBaz3());。它很好阅读,一眼就能理解... - glglgl
在2)中,如果getPlayer()返回的变量(如果它的返回值甚至与成员变量绑定!我们可能只会得到随机垃圾)不是final的(以及其他我不知道的修饰符和条件),JIT就无法知道没有其他线程改变了它的返回值。 - Zéychin
实例中存储的“Player”变量实质上只有在玩家选择采取改变其变量值的行动时才会被设置,并没有在任何预定点被设置。如果玩家选择加载游戏,则它会被覆盖,就像在开始新游戏时一样。 - Slendertron

0
除了可能不需要的 (Player) 强制转换之外,我甚至认为你的版本比长长的调用更好。
在我看来,如果你需要一个特殊的对象超过一两次,那么把它保存在本地变量中是值得的。
本地变量将需要一些堆栈上的字节,但另一方面,几个调用被省略了,所以你的版本显然胜出。

使用player cast是因为我的代码分为两个包。一个包含模板(BasePlayer),另一个包含实现(Player)。我的实例设置存储了一个BasePlayer,我可以在其中实现额外的功能,但是强制转换是必要的,以便利用特定于实现的功能。 - Slendertron

0

你最大的性能损失可能是对象函数查找:

(Player)Main.getInstance().getPlayer();

否则,如果可能的话,您希望最小化这些函数调用。在这种情况下,本地变量可以节省 CPU,但如果您有全局变量,则使用全局变量可能会更快一些。
不过,这实际上取决于循环中执行此操作的次数。通常情况下,在正常使用中两个方法都没有什么区别 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接