12

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

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实际上不是那么快,还是在某种程度上这是一个“不公平”的测试?

4

4 回答 4

15

这在某种程度上是不公平的比较。大部分 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 个,具体取决于编译器选择)。

于 2012-12-30T07:30:12.567 回答
5

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

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

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

于 2012-12-30T07:02:52.320 回答
2

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

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

于 2012-12-30T06:49:38.097 回答
1

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

于 2012-12-30T11:49:20.950 回答