SPOILER - 该问题包含在线裁判问题的解决方案。
我在LeetCode上解决了this个问题。
我的solution被接受,并且优于92%的提交。为了优化我的解决方案,我做了一个特别的改变。可以找到修改后的解决方案here。我做的唯一改变如下:
我不是每次都使用s.length()
(特别是19次出现),而是创建了一个新变量int len=s.length()
。
我期待性能有所改善,但新解决方案优于"只有69%的提交和#34;即性能下降25%。此外,虽然在第一种情况下所有测试用例都需要1ms才能通过,但在第二种情况下需要花费2ms,但差别虽然不大,但我完全没有想到。这背后的原因可能是什么?
答案 0 :(得分:4)
您的一般性问题的一般答案是:它取决于。
您看,真正的优化不是由Java编译器完成的。 java-to-bytecode编译器仅使用一组非常有限的已知优化技术;例如恒定折叠(将5 * 3变为15)。
真实" bang for the buck"发生在运行时,由即时编译器发生。而且你必须明白,这项技术可能需要15年多的深入研究。因此,根本没有办法在一个简单,全面的答案中告诉你JIT的能力。或者它究竟在做什么。从本质上讲,它会观察您的代码正在做什么;在有意义的地方,它将字节码转换为机器码。有时,它甚至会重做翻译;当它发现" thigns"已经改变了。
当然,正如一些评论所指出的那样:你对<#34>判断系统&#34;正在进行基准测试。您知道,使用Java进行基准测试是 hard 。因此,如果您对代码非常好奇:退一步,创建自己的度量套件(基于上面链接中的最佳做法)并测量您自己的数据点。
答案 1 :(得分:1)
如果您开始阅读Java字节代码,命令javap -p -s -v -c -constants Something.class
将变得非常方便。
通常在s.length()
方案中,您在另一个类上进行调用,创建一个不同的堆栈帧,用于评估该调用。
在int x = s.length()
方案中,您执行相同的调用,但是您进行了另外的调用以将整数存储在堆栈帧中的一个帧存储位置。
这意味着根据许多混杂因素重新使用存储的调用会变得更快或更慢。
s.length()
,您可能会在s.length()
编译或内联到调用堆栈之后触发热点调用优化的限制,从而加快执行速度s.length()
非常缓慢或复杂,或者您只打算调用几次,那么缓存该值可能会提高执行速度,因为热点可能会拒绝优化调用。这些当然是我在反转/组装/阅读代码之后开发的经验法则,并且像所有好的经验法则一样,在特定情况下几乎无用。 如果你有一个特定的场景,基准。 观察#1和#2中都有很多“ifs”,大部分时间我没有意识到哪些是没有基准。强>
考虑到这一点,通常,特别是在任何热点触发优化之前,方法中字节码操作的数量越少,通常运行得越快。此外,堆栈帧(预优化)更昂贵,您可能倾向于认为(CPU明智),但热点可以很好地减少最常用调用的成本。