基于模板参数的switch语句是否可以(或可以?)被编译器删除?
例如,请参阅以下函数,其中模板参数funcStep1
用于选择适当的函数。由于在编译时知道其参数,此switch语句会被删除吗?我试图从汇编代码(如下所示)中学习它,但是我还没有阅读汇编的经验,因此这对我来说并不是一个切实可行的任务。
由于新引入的if constexpr
提供了一个保证可以在编译时进行评估的选择,所以这个问题现在对我来说很重要。
template<int funcStep1>
double Mdp::expectedCost(int sdx0, int x0, int x1, int adx0, int adx1) const
{
switch (funcStep1)
{
case 1:
case 3:
return expectedCost_exact(sdx0, x0, x1, adx0, adx1);
case 2:
case 4:
return expectedCost_approx(sdx0, x0, x1, adx0, adx1);
default:
throw string("Unknown value (Mdp::expectedCost.cc)");
}
}
// Define all the template types we need
template double Mdp::expectedCost<1>(int, int, int, int, int) const;
template double Mdp::expectedCost<2>(int, int, int, int, int) const;
template double Mdp::expectedCost<3>(int, int, int, int, int) const;
template double Mdp::expectedCost<4>(int, int, int, int, int) const;
在使用gcc -O2 -ffunction-sections编译上述函数时,您会在这里找到'objdump -D'的输出:
1expectedCost.o: file format elf64-x86-64
Disassembly of section .group:
0000000000000000 <.group>:
0: 01 00 add %eax,(%rax)
2: 00 00 add %al,(%rax)
4: 08 00 or %al,(%rax)
6: 00 00 add %al,(%rax)
8: 09 00 or %eax,(%rax)
...
Disassembly of section .group:
0000000000000000 <.group>:
0: 01 00 add %eax,(%rax)
2: 00 00 add %al,(%rax)
4: 0a 00 or (%rax),%al
6: 00 00 add %al,(%rax)
8: 0b 00 or (%rax),%eax
...
Disassembly of section .group:
0000000000000000 <.group>:
0: 01 00 add %eax,(%rax)
2: 00 00 add %al,(%rax)
4: 0c 00 or $0x0,%al
6: 00 00 add %al,(%rax)
8: 0d .byte 0xd
9: 00 00 add %al,(%rax)
...
Disassembly of section .group:
0000000000000000 <.group>:
0: 01 00 add %eax,(%rax)
2: 00 00 add %al,(%rax)
4: 0e (bad)
5: 00 00 add %al,(%rax)
7: 00 0f add %cl,(%rdi)
9: 00 00 add %al,(%rax)
...
Disassembly of section .bss:
0000000000000000 <_ZStL8__ioinit>:
...
Disassembly of section .text._ZNK3Mdp12expectedCostILi1EEEdiiiii:
0000000000000000 <_ZNK3Mdp12expectedCostILi1EEEdiiiii>:
0: e9 00 00 00 00 jmpq 5 <_ZNK3Mdp12expectedCostILi1EEEdiiiii+0x5>
Disassembly of section .text._ZNK3Mdp12expectedCostILi2EEEdiiiii:
0000000000000000 <_ZNK3Mdp12expectedCostILi2EEEdiiiii>:
0: e9 00 00 00 00 jmpq 5 <_ZNK3Mdp12expectedCostILi2EEEdiiiii+0x5>
Disassembly of section .text._ZNK3Mdp12expectedCostILi3EEEdiiiii:
0000000000000000 <_ZNK3Mdp12expectedCostILi3EEEdiiiii>:
0: e9 00 00 00 00 jmpq 5 <_ZNK3Mdp12expectedCostILi3EEEdiiiii+0x5>
Disassembly of section .text._ZNK3Mdp12expectedCostILi4EEEdiiiii:
0000000000000000 <_ZNK3Mdp12expectedCostILi4EEEdiiiii>:
0: e9 00 00 00 00 jmpq 5 <_ZNK3Mdp12expectedCostILi4EEEdiiiii+0x5>
Disassembly of section .text.startup._GLOBAL__sub_I_expectedCost.cc:
0000000000000000 <_GLOBAL__sub_I_expectedCost.cc>:
0: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 7 <_GLOBAL__sub_I_expectedCost.cc+0x7>
7: 48 83 ec 08 sub $0x8,%rsp
b: e8 00 00 00 00 callq 10 <_GLOBAL__sub_I_expectedCost.cc+0x10>
10: 48 8b 3d 00 00 00 00 mov 0x0(%rip),%rdi # 17 <_GLOBAL__sub_I_expectedCost.cc+0x17>
17: 48 8d 15 00 00 00 00 lea 0x0(%rip),%rdx # 1e <_GLOBAL__sub_I_expectedCost.cc+0x1e>
1e: 48 8d 35 00 00 00 00 lea 0x0(%rip),%rsi # 25 <_GLOBAL__sub_I_expectedCost.cc+0x25>
25: 48 83 c4 08 add $0x8,%rsp
29: e9 00 00 00 00 jmpq 2e <_GLOBAL__sub_I_expectedCost.cc+0x2e>
Disassembly of section .init_array:
0000000000000000 <.init_array>:
...
Disassembly of section .comment:
0000000000000000 <.comment>:
0: 00 47 43 add %al,0x43(%rdi)
3: 43 3a 20 rex.XB cmp (%r8),%spl
6: 28 55 62 sub %dl,0x62(%rbp)
9: 75 6e jne 79 <_ZStL8__ioinit+0x79>
b: 74 75 je 82 <_ZStL8__ioinit+0x82>
d: 20 37 and %dh,(%rdi)
f: 2e 33 2e xor %cs:(%rsi),%ebp
12: 30 2d 32 37 75 62 xor %ch,0x62753732(%rip) # 6275374a <_ZStL8__ioinit+0x6275374a>
18: 75 6e jne 88 <_ZStL8__ioinit+0x88>
1a: 74 75 je 91 <_ZStL8__ioinit+0x91>
1c: 31 7e 31 xor %edi,0x31(%rsi)
1f: 38 2e cmp %ch,(%rsi)
21: 30 34 29 xor %dh,(%rcx,%rbp,1)
24: 20 37 and %dh,(%rdi)
26: 2e 33 2e xor %cs:(%rsi),%ebp
29: 30 00 xor %al,(%rax)
Disassembly of section .eh_frame:
0000000000000000 <.eh_frame>:
0: 14 00 adc $0x0,%al
2: 00 00 add %al,(%rax)
4: 00 00 add %al,(%rax)
6: 00 00 add %al,(%rax)
8: 01 7a 52 add %edi,0x52(%rdx)
b: 00 01 add %al,(%rcx)
d: 78 10 js 1f <.eh_frame+0x1f>
f: 01 1b add %ebx,(%rbx)
11: 0c 07 or $0x7,%al
13: 08 90 01 00 00 10 or %dl,0x10000001(%rax)
19: 00 00 add %al,(%rax)
1b: 00 1c 00 add %bl,(%rax,%rax,1)
1e: 00 00 add %al,(%rax)
20: 00 00 add %al,(%rax)
22: 00 00 add %al,(%rax)
24: 05 00 00 00 00 add $0x0,%eax
29: 00 00 add %al,(%rax)
2b: 00 10 add %dl,(%rax)
2d: 00 00 add %al,(%rax)
2f: 00 30 add %dh,(%rax)
31: 00 00 add %al,(%rax)
33: 00 00 add %al,(%rax)
35: 00 00 add %al,(%rax)
37: 00 05 00 00 00 00 add %al,0x0(%rip) # 3d <.eh_frame+0x3d>
3d: 00 00 add %al,(%rax)
3f: 00 10 add %dl,(%rax)
41: 00 00 add %al,(%rax)
43: 00 44 00 00 add %al,0x0(%rax,%rax,1)
47: 00 00 add %al,(%rax)
49: 00 00 add %al,(%rax)
4b: 00 05 00 00 00 00 add %al,0x0(%rip) # 51 <.eh_frame+0x51>
51: 00 00 add %al,(%rax)
53: 00 10 add %dl,(%rax)
55: 00 00 add %al,(%rax)
57: 00 58 00 add %bl,0x0(%rax)
5a: 00 00 add %al,(%rax)
5c: 00 00 add %al,(%rax)
5e: 00 00 add %al,(%rax)
60: 05 00 00 00 00 add $0x0,%eax
65: 00 00 add %al,(%rax)
67: 00 14 00 add %dl,(%rax,%rax,1)
6a: 00 00 add %al,(%rax)
6c: 6c insb (%dx),%es:(%rdi)
6d: 00 00 add %al,(%rax)
6f: 00 00 add %al,(%rax)
71: 00 00 add %al,(%rax)
73: 00 2e add %ch,(%rsi)
75: 00 00 add %al,(%rax)
77: 00 00 add %al,(%rax)
79: 4b 0e rex.WXB (bad)
7b: 10 5e 0e adc %bl,0xe(%rsi)
7e: 08 00 or %al,(%rax)
答案 0 :(得分:4)
是的,这是优化的。有一些事情可以使阅读汇编变得更容易,例如取消名称(例如:_ZNK3Mdp12expectedCostILi1EEEdiiiii
是double Mdp::expectedCost<1>(int, int, int, int, int) const
的错误形式),剥离注释和文本(并使用Intel语法):
double expectedCost<1>(int, int, int, int, int): # @double expectedCost<1>(int, int, int, int, int)
jmp expectedCost_exact(int, int, int, int, int) # TAILCALL
double expectedCost<2>(int, int, int, int, int): # @double expectedCost<2>(int, int, int, int, int)
jmp expectedCost_approx(int, int, int, int, int) # TAILCALL
double expectedCost<3>(int, int, int, int, int): # @double expectedCost<3>(int, int, int, int, int)
jmp expectedCost_exact(int, int, int, int, int) # TAILCALL
double expectedCost<4>(int, int, int, int, int): # @double expectedCost<4>(int, int, int, int, int)
jmp expectedCost_approx(int, int, int, int, int) # TAILCALL
以上网站为您简化了整个过程。
在这种情况下,我没有提供expectedCost_approx
的定义,因此编译器只留下了一个跳转。但是无论如何,编译器绝对足够聪明,以至于意识到每个模板函数在开关中都具有恒定值。
答案 1 :(得分:3)
您的问题的答案是:是的,任何中等有用的编译器都会perform dead code elimination。
if constexpr
并不是出于性能原因而强制执行编译时间评估。在性能方面,当给定表达式是编译时常量时,if constexpr
和普通if
之间并没有什么区别,因为编译器最终会以任何一种方式优化未使用的分支。 if constexpr
启用的功能是使非活动分支中的代码不得使用给定的模板参数实例化(例如,因为在特定情况下该代码无效)。对于上面的开关,将在所有情况下实例化整个代码。之后,优化器将删除未使用的代码。另一方面,if constexpr
确保未使用的分支中的代码永远不会实例化。有关更多信息,请参见here……
答案 2 :(得分:1)
我们没有switch constexpr
,即使有switch
值也没有保证消除简单constexpr
的分支(实际上是常规if
的保证) ,但我希望编译器会使用适当的优化标志将其删除。
还请注意,未使用的分支将实例化模板方法/对象(如果有的话),而if constexpr
则不会。
因此,如果您想确保只有相关的代码在那里,或者避免不必要的实例化,请使用if constexpr
。否则,请使用您发现的清洁剂。