C++ 为什么在c/c+中,交换机的优化方式与链接if else不同+;?

C++ 为什么在c/c+中,交换机的优化方式与链接if else不同+;?,c++,c,gcc,optimization,compiler-optimization,C++,C,Gcc,Optimization,Compiler Optimization,square的以下实现生成了一系列cmp/je语句,就像我期望的链式if语句一样: intsquare(intnum){ 如果(num==0){ 返回0; }else if(num==1){ 返回1; }else if(num==2){ 返回4; }else if(num==3){ 返回9; }else if(num==4){ 返回16; }else if(num==5){ 返回25; }else if(num==6){ 返回36; }else if(num==7){ 返回49; }否则{ 返回

square的以下实现生成了一系列cmp/je语句,就像我期望的链式if语句一样:

intsquare(intnum){
如果(num==0){
返回0;
}else if(num==1){
返回1;
}else if(num==2){
返回4;
}else if(num==3){
返回9;
}else if(num==4){
返回16;
}else if(num==5){
返回25;
}else if(num==6){
返回36;
}else if(num==7){
返回49;
}否则{
返回num*num;
}
}
下面将生成一个用于返回的数据表:

int square_2(int num){
开关(num){
案例0:返回0;
案例1:返回1;
案例2:返回4;
案例3:返回9;
案例4:返回16;
案例5:返回25;
案例6:返回36;
案例7:返回49;
默认值:返回num*num;
}
}
为什么gcc无法将顶部优化为底部

请参阅:


编辑:有趣的是,MSVC生成了一个跳转表,而不是开关箱的数据表。令人惊讶的是,clang将它们优化为相同的结果。

一个可能的理由是,如果
num
的低值更有可能,例如始终为0,则为第一个值生成的代码可能会更快。为开关生成的代码对所有值花费的时间相等

比较最佳案例,根据。有关该表的说明,请参阅

If
num==0
,对于“If”,您有xor、test、je(带跳转)、ret.Latency:1+1+跳转。但是,xor和test是独立的,因此实际执行速度将快于1+1个周期

如果
num<7
,对于“切换”,您有mov、cmp、ja(无跳转)、mov、ret。延迟:2+1+无跳转+2


不导致跳转的跳转指令比导致跳转的指令快。但是,该表没有定义跳转的延迟,因此我不清楚哪一个更好。有可能最后一个总是更好,GCC根本无法对其进行优化。

开关情况生成的代码通常使用跳转表。在这种情况下,通过查找表直接返回似乎是一种优化,利用了每个案例都涉及一个返回的事实。尽管该标准没有对此做出任何保证,但如果编译器为常规开关生成一系列比较,而不是跳转表,我会感到惊讶

现在来看
如果还有
,情况正好相反。当
开关案例
以恒定的时间执行时,不管分支的数量如何,
if-else
针对较小的分支数量进行了优化。在这里,您希望编译器基本上按照编写顺序生成一系列比较

因此,如果我使用了
if else
,因为我预计对
square()
的大多数调用都是针对
0
1
的,而很少针对其他值,那么对表查找进行“优化”实际上可能会导致我的代码运行速度比我预期的慢,从而破坏我使用
if
而不是
开关的目的。因此,尽管这是有争议的,但我觉得GCC做的是正确的,而clang在优化方面过于激进

在评论中,有人分享了一个链接,clang在该链接中为
if else
生成了基于查找表的代码。当我们使用clang将案例数量减少到两个(以及一个默认值)时,会发生一些值得注意的事情。它再次为if和switch生成相同的代码,但这次, 而不是查找表方法,这两种方法都适用。这意味着,即使是倾向于叮当声的开关也知道,当案例数量较少时,“如果”模式更为优化


总之,
if-else
的比较序列和
switch-case
的跳转表是编译器和开发人员在编写代码时通常遵循的标准模式。然而,对于某些特殊情况,一些编译器可能会选择打破这种模式,因为他们认为它提供了更好的优化。其他编译器可能只是选择坚持这种模式,即使显然是次优的,也要相信开发人员知道他想要什么。这两种方法都是有效的,各有优缺点。

你说的“未定义的行为”是什么意思?只要可观察到的行为相同,编译器就可以生成它所需要的任何程序集/机器代码wants@user207421忽略
返回
s;案例没有
中断
,因此开关也有特定的执行顺序。if/else链在每个分支中都有返回,本例中的语义是等价的。优化过程并非如此。作为反例,它不会优化任何函数。也许最简单的答案是。。。gcc只是还不能看到这个结构并对其进行优化。我同意@user1810087。您只需找到编译器优化过程的当前边界。当前未被(某些编译器)识别为可优化的子案例。事实上,并不是所有的if链都可以通过这种方式进行优化,而是只对同一变量进行常量测试的子集进行优化。尽管如此,用just-if语句替换代码并没有改善机器代码。另一方面,开关没有预定义的执行顺序,本质上只是一个美化的goto跳转表。也就是说,这里允许编译器对可观察到的行为进行推理,因此if-else版本的糟糕优化非常令人失望。嗯,有趣的理论,但对于ifs-vs-switch,您有:xor、test、jmp-vs-mov、cmp-jmp。三条指令,每条指令的最后一条是跳转指令。似乎是