如果与开关速度


Answers:


185

编译器可以在适用的情况下构建跳转表。例如,当您使用反射器查看生成的代码时,您会看到对于字符串上的大量开关,编译器实际上将生成使用哈希表调度这些代码的代码。哈希表使用字符串作为键,并将case代码委托为值。

它比许多链式if测试具有更好的渐近运行时间,并且即使相对较少的字符串,实际上也更快。


6
好的答案,关于哈希表很有趣。
BobbyShaftoe

4
在某些情况下,它们还转换为树比较。推理有些复杂,但基本上可以归结为表间接化,它使现代cpu跳转目标缓冲区无效,因此消除了分支预测器。我隐约记得在GCC会议上有关开关的代码源的一篇论文。
olliej

这意味着:切换(a)case“ x”:case“ y”:case“ z”://发生中断;}比以下方法快:if(a ==“ x” || a ==“ b” || a ==“ c”)//正确吗?
yazanpro 2012年

在这里,我们没有嵌套(如果没有的话),只有OR,那么您怎么看?
yazanpro 2012年

@yazanpro在旧的编译器上可能是(但是请注意,案例数量如此之小,可能无济于事!)。但是,现代编译器进行了更多的代码分析。结果,他们可能会发现这两个代码段是等效的,并应用相同的优化。但这只是我的推测,我不知道是否有任何编译器确实这样做。
康拉德·鲁道夫

15

这是一个略微的简化,因为通常任何现代编译器遇到的if..else if ..序列都可能被人轻易地转换为switch语句,编译器也将如此。但是只是为了增加乐趣,编译器不受语法的限制,因此可以在内部生成“ switch”之类的语句,这些语句混合了范围,单个目标等,并且它们可以(也可以)对switch和if做到这一点。 .else语句。

顺便说一句,Konrad答案的扩展是编译器可以生成一个跳转表,但这并不一定要保证(也不希望)。出于多种原因,跳转表对现代处理器上的分支预测器不利,而表本身对缓存行为不利。

switch(a) { case 0: ...; break; case 1: ...; break; }

如果编译器为此实际生成了一个跳转表,则if..else if..由于跳转表使分支预测失败,因此替代样式代码的运行速度可能会更慢。


4

不匹配的统计信息可能不好。

如果您实际下载源代码,则在if和switch情况下,no match值均已知为21。编译器应该能够抽象出来,知道应该始终运行哪个语句,并且CPU应该能够正确分支预测。

我认为,更有趣的情况是,并非所有情况都中断了,但这可能不是实验的范围。


4

Switch / case语句通常可以更快地达到1层深度,但是当您开始使用2级或2级以上时,switch / case语句开始的时间是嵌套if / else语句的2-3倍。

本文有一些速度比较,突出显示了嵌套此类语句时的速度差异。

例如,根据他们的测试,示例代码如下:

if (x % 3 == 0)
            if (y % 3 == 0)
                total += 3;
            else if (y % 3 == 1)
                total += 2;
            else if (y % 3 == 2)
                total += 1;
            else
                total += 0;
        else if (x % 3 == 1)
            if (y % 3 == 0)
                total += 3;
            else if (y % 3 == 1)
                total += 2;
            else if (y % 3 == 2)
                total += 1;
            else
                total += 0;
        else if (x % 3 == 2)
            if (y % 3 == 0)
                total += 3;
            else if (y % 3 == 1)
                total += 2;
            else if (y % 3 == 2)
                total += 1;
            else
                total += 0;
        else
            if (y % 3 == 0)
                total += 3;
            else if (y % 3 == 1)
                total += 2;
            else if (y % 3 == 2)
                total += 1;
            else
                total += 0;

在等效的switch / case语句运行一半的时间内完成了:

switch (x % 3)
    {
        case 0:
            switch (y % 3)
            {
                case 0: total += 3;
                    break;
                case 1: total += 2;
                    break;
                case 2: total += 1;
                    break;
                default: total += 0;
                    break;
            }
            break;
        case 1:
            switch (y % 3)
            {
                case 0: total += 3;
                    break;
                case 1: total += 2;
                    break;
                case 2: total += 1;
                    break;
                default: total += 0;
                    break;
            }
            break;
    case 2:
            switch (y % 3)
            {
                case 0: total += 3;
                    break;
                case 1: total += 2;
                    break;
                case 2: total += 1;
                    break;
                default: total += 0;
                    break;
            }
            break;
    default:
        switch (y % 3)
        {
            case 0: total += 3;
                break;
            case 1: total += 2;
                break;
            case 2: total += 1;
                break;
            default: total += 0;
                break;
        }
        break;
    }

是的,这是一个基本示例,但它说明了这一点。

因此,对于仅深一层的简单类型,可能得出结论使用switch / case,但对于更复杂的比较和多个嵌套级别,则使用经典的if / else构造吗?


-1:1.本文完全忽略了分支预测,2.算法并不完全相同(链接上的单个if-else编码已经过优化),并且3.发现的差异很小,没有任何借口使用正确的干净代码(在开关和相同的if-else构造之间的10.000.000调用中约4 ns)
Trojaner19年

由于开关模块的情况很少,因此无法优化该示例。通常在5-6个元素之后,它将生成一个跳转表。
antiduh

0

if over case的唯一优点是,第一种情况的发生频率显着增加。

不确定阈值的确切位置,但是除非第一个“几乎总是”通过第一个测试,否则我将使用案例语法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.