【问题标题】:Switch seems slower than ifSwitch 似乎比 if 慢
【发布时间】:2012-12-30 06:32:53
【问题描述】:

我对@9​​87654322@ 的速度很好奇,认为它“非常”快,但我有一个测试用例,似乎显示单个开关大约与大约 4 个if 测试一样快,当我预计(没有充分的理由)它会像 1 次测试一样快。这是我写的两个比较switchif的方法:

public static int useSwitch(int i) {
    switch (i) {
        case 1: return 2;
        case 2: return 3;
        case 3: return 4;
        case 4: return 5;
        case 5: return 6;
        case 6: return 7;
        default: return 0;
    }
}

public static int useIf(int i) {
    if (i == 1) return 2;
    if (i == 2) return 3;
    if (i == 3) return 4;
    if (i == 4) return 5;
    if (i == 5) return 6;
    if (i == 6) return 7;
    return 0;
}

这是我的测试代码:

long x = 0;
for (int i = 0; i < 999999; i++)
    x += useIf(i % 7); // I use "x" so calls won't get optimized out

useSwitch() 的另一个相同循环

在我的机器上,这些循环需要大约在同一时间才能完成,这令人惊讶。
我得出的 if 数为“4”,因为这是给定输入范围的平均值(我认为)。
如果我减少逻辑选项的数量,if 版本会明显更快。


我的问题是:

switch 是不是真的没有那么快,或者这在某种程度上是一个“不公平”的测试?

【问题讨论】:

  • 此链接可能会有所帮助-stackoverflow.com/questions/680656/…
  • JVM 的哪个版本?
  • 你是如何测量时间的?您是否在 -server 模式下运行?你的测试很可能毫无意义。
  • 这可能是不公平的。大部分 CPU 时间将用于处理模 (%),这会掩盖您在 switch 与 if() 中看到的任何差异。尝试改用 i % 8 ... JVM 可能会将其优化为相当于 i & ~7 的位掩码
  • 我知道我的回答没有回答您的问题,但在您的情况下,您可以通过保存和数组 int 或(枚举)来优化您的代码(我看到您正在寻找优化性能),而数组的索引是键->您的函数将返回 arr[index]

标签: java performance if-statement switch-statement


【解决方案1】:

这在某种程度上是不公平的比较。大部分 CPU 时间将用于处理模运算:i % 7。即使在最新最好的 CPU 上取模也非常慢,并且执行时间可能比您尝试进行基准测试的 if() 或 switch() 实现长 20 倍。

此外,有两种不同的方式可以优化 switch 语句。一种是查找表,它用于您提出的顺序情况。另一种方法是在适当的情况下使用搜索分区树。当案例稀疏时会发生这种情况,例如在以下示例中:

switch (someInt) {
    case 0:  ... break;
    case 10:  ... break;
    case 102:  ... break;
    case 6543:  ... break;
    case 19303:  ... break;
    case 19305:  ... break;
    // and so forth...
}

大多数编译器将使用展开的分区树来找到正确的情况,这在长开关上提供了非常好的平均和最坏情况跳转,需要命中正确的情况。生成的伪代码将是这样的:

if (someInt >= 6543) {
    if (someInt >= 19303) {
        // continue tree search, etc.
    }
    else if (someInt==6543) {}
}
else if (someInt >= 0) {
    if (someInt >= 10) {
        // continue tree search, etc.
    }
    else if (someInt == 0) {} 
}
else {
    // default case handler...
}

这里显示的只有 6-8 个案例并没有太大帮助,但如果您有一个可能有 50 多个案例的开关,那么它会非常有帮助。线性搜索将具有 O(n)(最差 50 个条件,平均 25 个),而分区树版本将接近 sqrt(n)(最差 8-9 个条件,平均 5-7 个,取决于编译器选择)。

【讨论】:

    【解决方案2】:

    switch应该更快,看字节码就够了

       TABLESWITCH
          1: L1
          2: L2
          3: L3
          4: L4
          5: L5
          6: L6
    

    看它是一个特殊的操作。在现实生活中,由于 JVM 优化,可能没有区别。但是,如果我们使用 -Xint(仅解释模式)运行您的代码,那么差异应该很明显,在我的 PC 上,它是 63 到 93(毫秒),有利于切换。

    【讨论】:

      【解决方案3】:

      这里有趣的问题是编译器如何翻译 switch(可能像哈希表或结果可能类似于 if-else 结构)。它还可以根据输入(如果已知)进行各种优化。但是,这取决于编译器,并且在我知道的任何规范中都没有定义。

      所以,我不能给你答案,但我可以告诉你,如果你想进一步完善你的测试,你总是可以给它输入 7。这将使它通过所有测试,直到返回一个答案,如果开关经过优化,它会给你更好的结果,如果它就像一个 if-else 它将有类似的结果。只是不要对 7 进行硬编码,以免编译器提前知道它,使用 parseint 之类的东西从字符串中获取它。

      【讨论】:

        【解决方案4】:

        由于您的代码可能会根据 JIT 的智能程度而被删除,也可能不会被删除,因此您可能已经确定 if 语句比 switch 更容易删除。这并不奇怪,因为许多 if 语句更有可能被消除,例如断言和调试检查。

        【讨论】:

          猜你喜欢
          • 2017-01-18
          • 1970-01-01
          • 1970-01-01
          • 2021-09-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-11
          • 1970-01-01
          相关资源
          最近更新 更多