逻辑内联与调用方法的效率?

Efficiency of having logic in-line vs calling a method?

我目前与大二的 JAVA 教授有分歧,希望大家能帮助解决:

我们开始的代码是这样的:

   public T peek()
   {
       if (isEmpty())
       .........
   }
   public boolean isEmpty() 
   {
       return topIndex<0;
   }

她希望我们删除 isEmpty() 引用并将其代码直接放入 if 语句中(即将 peek 方法内容更改为: if(topIndex<0).......) 到 "Make the code more efficient"。我认为 a) runtime/compile 时间优化器很可能会内联 isEmpty() call, b) 即使没有,5-10 台机器操作在几乎所有情况下都可以忽略不计,并且 c) 它只是糟糕的风格,因为它使程序的可读性和可变性降低。

所以,我想我的问题是: 与仅仅调用方法相比,内联逻辑是否可以提高运行时效率? 我尝试过简单的分析技术(又名长循环和秒表),但测试尚无定论。

编辑:

谢谢大家的回复!感谢大家抽出宝贵时间。另外,我感谢你们中那些评论与我的教授争论的实用主义的人,尤其是在没有数据的情况下这样做。 @Mike Dunlavey 我感谢您作为前教授的洞察力以及您对适当编码序列的建议。 @ya_pulser 我特别感谢您花时间分享的分析建议和链接。

正如你所说,差异会很小,在大多数情况下,可读性应该是一个更高的优先级。但是在这种情况下,由于额外的方法由一行组成,我不确定这会增加任何真正的可读性优势,除非您从其他地方调用相同的方法。

也就是说,请记住您的讲师的目标是帮助您学习计算机科学,这与编写生产代码的优先级不同。特别是,她不希望您将优化留给自动化工具,因为这对您的学习没有帮助。

此外,这只是一个实用的说明 - 在学校和专业发展中,我们都必须遵守我们个人不同意的编码标准。这是一项重要的技能,对于团队合作来说确实是必需的,即使它确实会引起摩擦。

调用 isEmpty 是惯用的且可读性很好。 手动内联这将是一个微优化, 在性能关键情况下最好做的事情, 在通过预期生产环境中的基准测试确认瓶颈后。

手动内联是否有真正的性能优势? 理论上是的,也许这就是讲座想要强调的。 在实践中, 我不认为你会找到一个绝对的答案。 自动内联行为可能依赖于实现。 还要记住,基准测试结果将取决于 JVM 实现、版本和平台。 出于这个原因, 这种优化在罕见的极端情况下很有用, 并且通常不利于可移植性和可维护性。

按照同样的逻辑,我们是否应该内联所有方法, 以复制大块代码为代价消除所有间接寻址? 当然不。 分解和内联之间的确切界限也可能取决于个人品味, 在某种程度上。

你对 java 代码行为的假设是正确的,但你对你的教授在没有数据的情况下争论是不礼貌的:)。没有数据的争论是没有意义的,用测量和图表证明你的假设。

您可以使用 JMH ( http://openjdk.java.net/projects/code-tools/jmh/ ) 创建一个小的基准并衡量两者之间的差异:

  • 手动内联(删除 isEmpty 方法并将代码放在调用位置)
  • 由 java jit 编译器内联(100k (?) 次调用后的热点 - 请参阅 jit print 编译输出)
  • 完全禁用热点内联

请阅读http://www.oracle.com/technetwork/java/whitepaper-135217.html#method

有用的参数可能是:

  • -Djava.compiler=NONE
  • -XX:+打印编译

此外,每个 jdk 版本都有自己的一组参数来控制 jit。

如果您将创建一些图形作为您的研究成果,并礼貌地将它们呈现给教授 - 我认为这对您将来会有所帮助。

我认为https://whosebug.com/users/2613885/aleksey-shipilev可以帮助解决jmh相关问题

顺便说一句:当我将大量方法内联到一个巨大的代码循环中以实现神经网络反向传播例程的最大速度时,我取得了巨大的成功,因为 java 是(曾经?)懒得将方法与方法内联与方法。它无法维护且速度很快:(.

悲伤...

我同意你的直觉,尤其是 "the 5-10 machine operations would be negligible in nearly every situation"。

我是 C.S。很久以前的教授。 一方面,教授们需要你能给他们的所有懈怠。 教学要求很高。你不会有糟糕的一天。 如果你参加了 class 而你没有做好充分准备,那你就会遇到麻烦。 如果您在周五进行测试但周一没有成绩,学生会说 "But you had all weekend!" 看到你的学生学习,你会感到满足,但你自己并没有学到多少,除了如何教。

另一方面,很少有教授对真正的软件有太多的实践经验。 所以他们的意见往往建立在各种教条的确定性之上,而不是坚实的实用主义。

性能就是一个很好的例子。 他们倾向于说 "Don't do X. Do Y because it performs better.",这完全忽略了性能问题的要点——你必须处理 分数 ,而不是 绝对值 。一切都取决于 else 发生的事情。 正如有人所说,接近性能的方法是 "First make it right. Then make it fast."

而使它变快的方法不是通过目测代码(并想知道 "should I do this, or should I do that"),而是通过 运行 它并让它告诉您它是如何花费时间的。 剖析 的基本思想是你如何做到这一点。 现在有不好的侧写和好的侧写这样的东西,as explained in the second answer here(通常当教授 教侧写时,他们教的是坏侧写),但这就是要走的路.

要查看的另一种数据形式可能是生成的代码。见-XX:+PrintAssembly选项和朋友。有关详细信息,请参阅 How to see JIT-compiled code in JVM?

我相信在这种特殊情况下,Hotspot JVM 会内联对 isEmpty 的调用,并且不会有性能差异。