Включить аргумент шаблона: gcc удаляет переключатель? - PullRequest
2 голосов
/ 17 апреля 2019

Компилятор удалит (или может?) Оператор 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;

Здесь вы найдете выходные данные 'objdump -D', когда вышеуказанная функция скомпилирована с gcc -O2 -ffunction-section:

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 ]

4 голосов
/ 17 апреля 2019

Да, это оптимизировано.Есть несколько вещей, которые облегчают чтение на ассемблере, такие как разбор имен (пример: _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

https://godbolt.org/z/ZtoKFH

Вышеуказанный сайт упрощает весь этот процесс для вас.

В этом случае я не предоставил определения для expectedCost_approx, поэтому компилятор просто оставляет переход.Но в любом случае компиляторы определенно достаточно умны, чтобы понимать, что каждая функция шаблона имеет постоянное значение в переключателе.

3 голосов
/ 17 апреля 2019

Ответ на ваш вопрос: да, любой умеренно полезный компилятор будет выполнять удаление мертвого кода .

if constexpr - это не столько принудительная оценка времени компиляции по причинам производительности. С точки зрения производительности, на самом деле не будет никакой разницы между if constexpr и нормальным if, когда данное выражение является константой времени компиляции, потому что компиляторы в конечном итоге будут оптимизировать неиспользуемую ветвь в любом случае. Что позволяет if constexpr, так это иметь код в неактивной ветви, который не должен создаваться с заданными аргументами шаблона (например, потому что он будет недействительным в этом конкретном случае). Для вашего переключения выше, весь код будет создан для всех случаев. Только после этого неиспользуемый код будет удален оптимизатором. if constexpr с другой стороны, гарантирует, что код в неиспользуемой ветви никогда не будет создаваться с самого начала. См., Например, здесь , чтобы узнать больше об этом & hellip;

1 голос
/ 17 апреля 2019

У нас нет switch constexpr, и для простого switch нет гарантии исключения ветвей, даже со значением constexpr (как на самом деле для обычного if), но я ожидаю, что компилятор удалитих с правильным флагом оптимизации.

Обратите также внимание, что ваши неиспользуемые ветки будут создавать экземпляры, если таковые имеются, шаблонные методы / объекты, тогда как if constexpr не будет.

Так что, если вы хотите получить гарантиючто существует только соответствующий код или во избежание ненужных реализаций используйте if constexpr.Еще используйте тот, который вы найдете яснее.

...