3

选项 1:

  boolean isFirst = true;
  for (CardType cardType : cardTypes) {
    if (!isFirst) {
      descriptionBuilder.append(" or ");
    } else {
      isFirst = false;
    }
    //other code not relevant to this theoretical question
  }

选项 2:

boolean isFirst = true;
for (CardType cardType : cardTypes) {
  if (!isFirst) {
    descriptionBuilder.append(" or ");
  } 
  isFirst = false;
  //other code not relevant to this theoretical question
}

我的分析:两个代码具有相同的语义。

第一个代码)我不确定这段代码是有两个分支(就分支预测器而言)还是一个分支。我正在查看http://en.wikipedia.org/wiki/X86_instruction_listings但无法弄清楚是否有一个 X86 指令类似于“如果先前的条件值是假跳转那里”,以避免两个分支预测(非常糟糕)

第二个代码)最有可能总是执行简单的 MOV(寄存器或元素很可能已经在缓存中),这是相对便宜的(最多几个周期)

所以,我的观点是,除非处理器解码成微码指令可以做一些智能的事情或存在 X86 指令以避免必要的分支预测,否则第二个代码更快。

我知道这纯粹是理论上的问题,因为在实践中,这个分支可以使应用程序更快 0.000000002% 或类似的东西。

我错过了什么?

编辑:我添加了一个循环,为有问题的分支提供更多“权重”

EDIT2:问题是关于分支预测的英特尔架构(奔腾和更新的处理器)。

4

6 回答 6

3

该代码具有相同的效果,但不会产生相同的字节码或程序集(可能)。

这在性能方面有多大差异,尚不清楚并且可能微不足道。

重要的是代码的清晰性。由于在这样的简单情况下代码更难推理,我看到了更多的错误和性能问题。

简而言之,对您来说最清晰和最简单的东西也可能足够快,或者最容易修复。

于 2015-02-04T09:23:51.287 回答
3

使用JMH给出以下数字,cardTypes 数组大小为 10,整数增量作为逻辑(Java 15 / AMD 3950X / Windows 10):

Benchmark          Mode  Cnt          Score         Error  Units
Benchmark.option1  thrpt   25  273369417.720 ± 1618952.179  ops/s
Benchmark.option2  thrpt   25  273415784.192 ±  852618.585  ops/s

“选项 2”的平均性能大约快 0.017% (YMMV)。

另请参阅:分支预测方法分派内存访问吞吐量和延迟垃圾收集

于 2021-03-07T21:43:03.813 回答
2

不同的硬件对每条汇编指令都有不同的成本,在现代硬件上,由于流水线和缓存的影响,甚至一条指令的成本也难以预测。

从您的孤立示例中,流水线和缓存上的 if 和 if/else 之间的区别并不清楚。如果您运行该代码一次,您就不太可能看到任何差异。在一个紧密的循环中反复运行它,if 本身的性能将由 a) 检查的成本和 b) 检查结果的可预测性支配。换句话说,分支预测将成为主导因素,并且不会受到 if 或 if/else 代码块的影响。

可以在此处阅读有关分支预测效果的精彩讨论为什么处理排序数组比处理未排序数组更快?(见得分最高的答案)。

于 2015-02-04T09:58:05.563 回答
1

假设您的代码片段是 for 循环中的 if 块。Hotspot 具有展开 for 循环的能力,这包括进行常见的“是循环的第一次迭代”检查并将其内联到循环之外。从而避免在循环的每次迭代中重新检查条件的成本。从而避免关注 if 或 if/else 哪个更快。

Oracle在此处记录此行为

于 2015-02-04T10:16:18.893 回答
0

两个代码具有相同的语义。

否 两个代码不同,

如果您的条件不匹配,第一个代码应用程序 isFirst = false;将标志设置为 false 。if (!isFirst)

每次第二个代码都将您的标志更改为是否false满足条件。

于 2015-02-04T09:21:10.387 回答
0

一个构造中有两个分支if/else:顶部的条件分支和else部件末端的if部件周围的分支。该else部分中没有分支,至少在任何具有中等能力实现的编译器中都没有。

与此相反,您必须平衡始终执行isFirst = false;生产线的成本。

在您提到的特定情况下,与方法调用的成本相比,它不太可能产生丝毫差异。

于 2015-02-04T09:29:38.737 回答