Java超级调优,几个问题

6
在我提出问题之前,能否请您不要对无缘由的优化进行演讲。以下问题仅为学术性质。
我一直在思考Java中根(即经常使用且经常彼此访问)类之间访问的效率问题,但这适用于大多数面向对象语言/编译器。我猜想最快的方式是使用static final引用来访问Java中的某些内容。理论上,由于该引用在加载时可用,因此一个好的JIT编译器将消除任何引用查找以访问变量,并将任何访问指向该变量的恒定地址。也许出于安全原因,它并不起作用,但请听我说...
假设我已经决定存在一些操作顺序问题或一些必须在启动时传递的参数,这意味着即使我费心让每个类构造另一个类(建议Java类对彼此具有静态final引用),我也不能拥有静态final引用。我可能不想这样做的另一个原因是...哦,例如,我正在为某些这些类的平台特定实现提供支持。;-)
现在我只剩下两个明显的选择。我可以让我的类通过静态引用(在某个系统中央类上)了解彼此,在构造完所有类之后设置该引用(在此期间,我规定它们不能相互访问,从而至少在构造期间消除操作顺序问题)。另一方面,如果我现在决定解决操作顺序问题或将其交由传递参数的人负责,或更重要的是为这些想要相互引用的类提供平台特定实现,那么这些类可以具有实例最终引用。
静态变量意味着您不必查找变量所属类的位置,从而节省了一个操作。最终变量意味着您根本不必查找值,但它必须属于您的类,因此您可以节省“一个操作”。好吧,我知道我现在有点牵强附会!
然后我想到了其他事情:我可以使用static final存根类,类似于奇怪的接口,其中每个调用都被委派给一个“impl”,该“impl”只能扩展存根。然后可能需要进行双重函数调用来运行功能,并且我猜您可能不能再声明方法为final。我假设如果适当声明,则它们可能会被内联,然后放弃,因为我意识到我现在必须考虑是否可以使对“impl”的引用成为静态的,最终的,或...
那么这三者中哪个会最快呢?:-)
还有其他降低频繁访问开销的想法或向JIT编译器提示性能的其他方法吗?
更新:在运行各种测试并阅读http://www.ibm.com/developerworks/java/library/j-jtp02225.html之后,我发现大多数调优时通常会看的东西,例如C ++,与JIT编译器完全无关。我曾经看到它一次、两次地运行30秒的计算,但第三次(以及随后的运行)决定“嘿,你没有读取那个计算的结果,所以我不运行它!”。
顺便说一下,您可以测试数据结构,我能够使用微基准测试开发出更适合我的需要的arraylist实现。访问模式必须足够随机,以使编译器猜测,但它仍然可以通过我的更简单和更调整的代码更好地实现一个泛型化的增长数组。
就这里的测试而言,我根本无法获得基准测试结果!从一个终极 vs 非终极对象引用调用函数并读取变量的简单测试揭示了比JVM的访问模式更多的JIT信息。难以置信的是,在方法中的不同位置调用相同的函数和相同的对象会使花费的时间因四倍而异!
正如IBM文章中的人所说,测试优化的唯一方法是在现场测试。
感谢所有指引我的人。

2
编写自己的微基准测试,使用“如何在Java中编写正确的微基准测试?”的答案作为指南。 - tgdavies
3个回答

1
值得注意的是,静态字段存储在一个特殊的按类别对象中,该对象包含该类别的静态字段。使用静态字段而不是对象字段不太可能更快。

它可能不会显著地更快,但是静态对象难道不具有已知地址吗?而对象的字段必须从对象的地址计算出来。这是我所知道的C++思想,但JVM/JIT还能以哪些高效的方式实现它呢? - user595447
1
它的地址与任何其他对象一样不再知道。一个类可以随着其静态字段一起被加载和卸载。从性能角度来看,确保您的数据对缓存友好比其他方面更有影响。 - Peter Lawrey

1
看到更新了吗?我通过一些基准测试回答了自己的问题,并发现在意想不到的领域有更大的收益,而在像引用成员这样的简单操作的性能方面,在大多数现代系统上是可比的,其中性能受限于内存带宽而不是 CPU 周期。

0

假设您找到了一种可靠地对应用程序进行分析的方法,请记住,如果您切换到另一个jdk实现(IBM到Sun到OpenJDK等),甚至在现有JVM上升级版本,所有这些都将失效。

您遇到问题的原因,以及在不同的JVM实现中可能会得到不同结果的原因在于Java规范 - 明确指出它不定义优化,并且让每个实现自行优化(或不优化),只要执行行为不受优化的影响。


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