打开模板参数:gcc是否删除了交换机?

问题描述 投票:2回答:3

将(或可以?)基于模板参数的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)
c++ templates switch-statement
3个回答
4
投票

是的,这是优化的。有一些东西可以使阅读程序集更容易,例如demangling名称(例如:_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的定义,所以编译器只是跳了一下。但无论如何,编译器肯定足够聪明,可以意识到每个模板函数在交换机中都有一个常量值。


3
投票

您的问题的答案是:是的,任何适度有用的编译器将perform dead code elimination

if constexpr并不是因为性能原因而强制编译时评估。在性能方面,当给定的表达式是编译时常量时,if constexpr和普通的if之间没有任何区别,因为编译器最终会以任何方式优化未使用的分支。 if constexpr启用的是在非活动分支中具有不能用给定模板参数实例化的代码(例如,因为在该特定情况下它将是无效的)。对于上面的开关,将为所有情况实例化整个代码。之后,优化程序将删除未使用的代码。另一方面,if constexpr保证未使用的分支中的代码永远不会被实例化。有关详细信息,请参阅here


1
投票

我们没有switch constexpr,并且对于简单的switch没有分支消除的保证,即使使用constexpr值(事实上常规的if),但我希望编译器会使用适当的优化标志删除它们。

另请注意,未使用的分支将实例化模板方法/对象(如果有),而if constexpr则不会。

因此,如果您希望保证只有相关代码,或者避免不必要的实例化,请使用if constexpr。否则使用你发现更清晰的那个。

© www.soinside.com 2019 - 2024. All rights reserved.