Java:Getter 和 setter 比直接访问更快?

2022-09-01 10:28:39

我测试了我正在Linux Netbook上使用VisualVM 1.3.7编写的Java光线跟踪器的性能。我用分析器测量。
为了好玩,我测试了使用 getter 和 setter 与直接访问字段之间是否存在差异。getters 和 setter 是标准代码,无需添加。

我没想到有任何差异。但直接访问代码的速度较慢。

以下是我在Vector3D中测试的示例:

public float dot(Vector3D other) {
    return x * other.x + y * other.y + z * other.z;
}

时间: 1542 毫秒 / 1,000,000 次调用

public float dot(Vector3D other) {
    return getX() * other.getX() + getY() * other.getY() + getZ() * other.getZ();
}

时间: 1453 毫秒 / 1,000,000 次调用

我没有在微基准测试中测试它,而是在光线追踪器中测试它。我测试代码的方式:

  • 我用第一个代码启动了程序并进行了设置。光线追踪器尚未运行。
  • 我启动了探查器,并在初始化完成后等待了一段时间。
  • 我启动了光线追踪器。
  • 当 VisualVM 显示足够的调用时,我停止了探查器并稍等了一会儿。
  • 我关闭了光线追踪程序。
  • 我将第一个代码替换为第二个代码,并在编译后重复上述步骤。

我确实为这两个代码运行了至少20,000,000次调用。我关闭了任何不需要的程序。我将 CPU 设置为性能,因此我的 CPU 时钟始终处于最大值。
第二个代码怎么可能快6%?


答案 1

我确实对大量的JVM预热进行了一些微基准测试,发现这两种方法的执行时间完全相同

发生这种情况是因为 JIT 编译器将 getter 方法与对字段的直接访问内联在一起,从而使它们具有相同的字节码。


答案 2

谢谢大家帮我回答这个问题。最后,我找到了答案。

首先,Bohemian是对的:使用PrintAssembly,我检查了生成的装配代码是相同的假设。是的,尽管字节码不同,但生成的代码是相同的。
所以masterxilo是对的:分析器必须是罪魁祸首。但是masterxilo关于定时围栏和更多检测代码的猜测不可能是真的。这两个代码最终是相同的。

因此,仍然存在一个问题:在探查器中,第二个代码似乎快了6%?

答案在于 VisualVM 的测量方式:在开始分析之前,您需要校准数据。这用于消除由探查器引起的开销时间。
虽然校准数据是正确的,但测量的最终计算却不是。VisualVM 在字节码中看到方法调用。但它没有看到 JIT 编译器在优化时删除这些调用。
因此,它消除了不存在的开销时间。这就是差异的出现方式。


推荐