Visual C++中的开关如何编译?它有多快和优化?

32

我发现在C++的switch语句中只能使用数字值,因此我认为它与一组if-else语句之间必须存在某些更深层次的差异。

因此,我问了自己以下问题:

  • switchif-elseif-elseif在运行时速度、编译时优化和一般编译方面有何区别?我主要在谈论MSVC。

MSVC可以像其他优秀的优化编译器一样生成查找表。它甚至可以生成位查找表。MSVC C++编译器是否将switch语句转换为查找表以提高效率?,并且它甚至可以生成位查找表 - phuclv
2个回答

43

交换机通常被编译成跳转表(先进行一次比较以找出要运行的代码),如果不可能,编译器仍将重新排列比较操作,以便在这些值中执行二分查找(log N比较操作)。if-else链是一种线性搜索(尽管,我猜想,如果所有相关值都是编译时整数常量,编译器原则上可以执行类似的优化)。


1
我将利用这个机会抨击if-then-elseif链。多于一个“else if”是DDD(设计缺陷障碍)的标志。 - Amardeep AC9MF
5
这是一份实际的C++ switch语句实现代码,适用于VisualC++。原文链接为:http://www.codeproject.com/Articles/100473/Something-You-May-Not-Know-About-the-Switch-Statem。 - David Andreoletti
知道这是否被优化过会很好,而不是他们是否能够。 - Lothar
如果情况可以/已经被排序,跳表是否和二分查找一样好? - Opux
@Opux 跳转表更快:只需要一次查询,而不是 log n 次比较。然而,你需要访问内存,这可能会使其运行时间变慢。 - Flamefire

9

Switch语句通常是编译器优化的常见来源。也就是说,它们的处理方式取决于您在编译器上使用的优化设置。

编译Switch语句的最基本(未经过优化的)方法是将其视为一系列if ... else if ...语句。编译器通常优化Switch语句的方法是将其转换为跳转表,其可以类似于:

if (condition1) goto label1;
if (condition2) goto label2;
if (condition3) goto label3;
else            goto default;
label1:
  <<<code from first `case statement`>>>
  goto end;
label2:
  <<<code from first `case statement`>>>
  goto end;
label3:
  <<<code from first `case statement`>>>
  goto end;
default:
  <<<code from `default` case>>>
  goto end;
end:

这种方法更快的一个原因是条件语句中的代码更少(所以如果条件预测错误,指令缓存惩罚就更小)。此外,“穿透”情况变得更加容易实现(编译器省略了 goto end 语句)。
编译器可以通过创建指向标记位置的指针数组,并将您要切换的值用作该数组的索引来进一步优化跳转表。这将从代码中消除几乎所有的条件语句(除了需要验证您要切换的值是否与您的案例之一匹配所需的任何内容)。
需要注意的是:嵌套跳转表很难生成,有些编译器甚至拒绝尝试创建。因此,如果对您而言最大程度地优化代码很重要,请避免在另一个 switch 中嵌套一个 switch (我不确定 MSVC 特别处理嵌套的 switch 的方式,但编译器手册应该会告诉您)。

一个注意点:我没有提及关于开关性能相对于if-else树的具体数字,因为优化的好处严重依赖于代码。唯一真正的方法是编写两种方式的代码并测量运行每个代码所需的时间,以确定你从优化中获得了多少改进。 - bta
18
这段代码不是跳转表,实际上它根本不代表任何表。然而,维基百科的链接提供了正确的例子。 - dualed

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接