打开模板参数:gcc是否会删除该开关?

时间:2019-04-17 11:16:59

标签: c++ templates switch-statement

基于模板参数的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)

3 个答案:

答案 0 :(得分:4)

是的,这是优化的。有一些事情可以使阅读汇编变得更容易,例如取消名称(例如:_ZNK3Mdp12expectedCostILi1EEEdiiiiidouble 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

https://godbolt.org/z/ZtoKFH

以上网站为您简化了整个过程。

在这种情况下,我没有提供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。否则,请使用您发现的清洁剂。