由于编译器优化,switch语句通常比等效的if-else-if语句更快(例如在article中描述)。
这种优化实际上如何运作?有没有人有一个很好的解释?
答案 0 :(得分:176)
编译器可以在适用的情况下构建跳转表。例如,当您使用反射器查看生成的代码时,您将看到对于字符串上的大型开关,编译器实际上将生成使用哈希表来分派这些代码的代码。哈希表使用字符串作为键,并将case
代码委托为值。
这比许多链式if
测试具有渐近更好的运行时间,即使对于相对较少的字符串实际上也更快。
答案 1 :(得分:29)
Konrad是正确的。在切换连续的整数范围的情况下(例如,你有案例0,案例1,案例2 ..案例n),编译器可以做更好的事情,因为它甚至不需要构建哈希表;它只是存储一个函数指针数组,因此可以在恒定时间内加载其跳转目标。
答案 2 :(得分:13)
这是一种轻微的简化,因为通常任何遇到if..else if ..
序列的现代编译器都可以由一个人轻易地转换为switch语句,编译器也是如此。但是为了增加额外的乐趣,编译器不受语法的限制,因此可以在内部生成具有混合范围,单个目标等的“切换”语句 - 并且它们可以(并且确实)为交换机和if执行此操作。 .else声明。
Anyhoo,对Konrad的答案的扩展是编译器可能会生成一个跳转表,但这不一定得到保证(也不可取)。由于各种原因,跳转表会对现代处理器上的分支预测器做坏事,而表本身也会对缓存行为做坏事,例如。
switch(a) { case 0: ...; break; case 1: ...; break; }
如果编译器为此实际生成了一个跳转表,那么替换if..else if..
样式代码可能会慢一些,因为跳转表会破坏分支预测。
答案 3 :(得分:5)
正如Konrad所说,编译器可以构建一个Jump表。
在C ++中,原因可能是由于交换机的限制。
答案 4 :(得分:4)
不匹配的统计数据可能不太好。
如果您实际下载了源,则在if和switch的情况下,已知无匹配值为21。编译器应该能够抽象出来,知道应该始终运行哪个语句,并且CPU应该能够正确地进行分支预测。
更有趣的案例是,在我看来,并非每个案例都会破裂,但这可能不是实验的范围。
答案 5 :(得分:4)
Switch / case语句通常可以更快1级,但是当你开始进入2级或更多时,switch / case语句的开始时间是嵌套的if / else语句的2-3倍。
This article has some speed comparisons突出显示嵌套此类语句时的速度差异。
例如,根据他们的测试,示例代码如下:
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语句运行时 half 完成:
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结构?
答案 6 :(得分:0)
if over case的唯一优点是当第一种情况的出现频率明显增加时。
不确定阈值的确切位置,但我使用case语法,除非第一个“几乎总是”通过第一个测试。