C ++, регистр переключателя и компилятор - PullRequest
3 голосов
/ 30 марта 2012

Из того, что я прочитал в различных ответах на похожие вопросы, в некоторых случаях варианты переключения компилируются по-разному.

Я имею в виду несколько сценариев, но я не уверен, как они будут компилироваться.

Сценарий 1

Случай переключателя, который принимает значение enum. Случаи варьируются от 0 до 99 и заказываются.

Сценарий 2

Случай переключателя, который принимает значение enum. Случаи варьируются от, скажем, 0–30, 50–80, 100–150. Неупорядоченные. Будет ли это компилироваться иначе, чем в сценарии выше?


По сути, я хотел бы знать, как будут скомпилированы варианты переключения в сценариях и существуют ли различия между этими двумя сценариями. Спасибо!

edit: я должен был упомянуть, что одной из моих самых больших проблем является то, сколько проверок потребуется для соответствия случаю. Если операторы линейны, то для первого сценария, если бы это было if-else-if, потребовалось бы не более 100 проверок, если я не ошибаюсь. Но как это происходит с коммутаторами? Какую оптимизацию выполняет компилятор?

Ответы [ 4 ]

8 голосов
/ 30 марта 2012

Невозможно сказать вообще. Стандарт C ++ делает едва ли какие-либо предположения о генерируемом коде. Это означает, что компилятор может почти (да, есть исключения) переупорядочивать код, если семантика остается неизменной. Оптимизацию часто называли «черной магией компиляции», когда я проходил курсы по этой теме.

Имея это в виду, ваш компилятор может выдавать один и тот же или другой код. Он может выплевывать один и тот же код для некоторых уровней оптимизации и совершенно другой код для других. Это может полностью зависеть от кода, который вы используете в самих блоках case. Если у вас есть один и тот же код в двух точках, он может просто оптимизировать его для однократного использования этого кода, а может и нет.

Единственный способ узнать, что произойдет, - посмотреть на вывод компилятора с определенными фиксированными флагами и посмотреть, что произойдет. И как только вы узнаете, что происходит, не надейтесь на это, это может измениться в следующей версии компилятора.

Если вы программируете, всегда соблюдайте гарантии, которые дает вам стандарт языка, и никогда не принимайте ничего сверх этого.

5 голосов
/ 30 марта 2012

Теоретически для полного переключения компилятор сгенерирует таблицу переходов, то есть массив указателей на метки для прямой индексации (при условии, что значение соответствует диапазону).

Для менее полной версии, компилятор должен балансировать таблицу переходов и операторы ветвления. Вероятно, это также будет зависеть от настроек: увеличите ли вы скорость с помощью -O3 или уменьшите размер с помощью -Oz, что определенно повлияет на некоторые крайние случаи.

Наконец, коммутатор имеет только один регистр, вероятно, вместо этого он будет представлен как ветвь.

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


Демонстрация (с помощью веб-сайта Try out LLVM)

enum Enum {
  Zero,
  One,
  Two,
  Three,
  Four,
  Five,
  Six,
  Seven,
  Eight,
  Nine
};

char const* full(enum Enum e) {
  switch(e) {
  case Zero: return "Zero";
  case One: return "One";
  case Two: return "Two";
  case Three: return "Three";
  case Four: return "Four";
  case Five: return "Five";
  case Six: return "Six";
  case Seven: return "Seven";
  case Eight: return "Eight";
  case Nine: return "Nine";
  }

  return "Uh ?";
}

char const* sparse(enum Enum e) {
  switch(e) {
  default: return "Not handled";
  case Zero: return "Zero";
  case One: return "One";
  case Two: return "Two";
  case Seven: return "Seven";
  case Eight: return "Eight";
  case Nine: return "Nine";
  }
}

char const* null(enum Enum e) {
  switch(e) {
  default: return "Not Zero";
  case Zero: return "Zero";
  }
}

Функция full скомпилирована в:

    .text
    .globl  full
    .align  16, 0x90
    .type   full,@function
full:                                   # @full
.Ltmp0:
    .cfi_startproc
# BB#0:
    cmpl    $9, %edi
    ja  .LBB0_11
# BB#1:
    movl    %edi, %ecx
    movl    $.L.str, %eax
    jmpq    *.LJTI0_0(,%rcx,8)
.LBB0_2:
    movl    $.L.str1, %eax
    ret
.LBB0_3:
    movl    $.L.str2, %eax
    ret
.LBB0_4:
    movl    $.L.str3, %eax
    ret
.LBB0_5:
    movl    $.L.str4, %eax
    ret
.LBB0_6:
    movl    $.L.str5, %eax
    ret
.LBB0_7:
    movl    $.L.str6, %eax
    ret
.LBB0_8:
    movl    $.L.str7, %eax
    ret
.LBB0_9:
    movl    $.L.str8, %eax
    ret
.LBB0_10:
    movl    $.L.str9, %eax
    ret
.LBB0_11:
    movl    $.L.str10, %eax
.LBB0_12:
    ret
.Ltmp1:
    .size   full, .Ltmp1-full
.Ltmp2:
    .cfi_endproc
.Leh_func_end0:
    .section    .rodata,"a",@progbits
    .align  8
.LJTI0_0:
    .quad   .LBB0_12
    .quad   .LBB0_2
    .quad   .LBB0_3
    .quad   .LBB0_4
    .quad   .LBB0_5
    .quad   .LBB0_6
    .quad   .LBB0_7
    .quad   .LBB0_8
    .quad   .LBB0_9
    .quad   .LBB0_10

И функция sparse до:

    .text
    .globl  sparse
    .align  16, 0x90
    .type   sparse,@function
sparse:                                 # @sparse
.Ltmp3:
    .cfi_startproc
# BB#0:
    movl    $.L.str11, %eax
    cmpl    $9, %edi
    ja  .LBB1_8
# BB#1:
    movl    %edi, %ecx
    jmpq    *.LJTI1_0(,%rcx,8)
.LBB1_2:
    movl    $.L.str, %eax
    ret
.LBB1_3:
    movl    $.L.str1, %eax
    ret
.LBB1_4:
    movl    $.L.str2, %eax
    ret
.LBB1_5:
    movl    $.L.str7, %eax
    ret
.LBB1_6:
    movl    $.L.str8, %eax
    ret
.LBB1_7:
    movl    $.L.str9, %eax
.LBB1_8:
    ret
.Ltmp4:
    .size   sparse, .Ltmp4-sparse
.Ltmp5:
    .cfi_endproc
.Leh_func_end1:
    .section    .rodata,"a",@progbits
    .align  8
.LJTI1_0:
    .quad   .LBB1_2
    .quad   .LBB1_3
    .quad   .LBB1_4
    .quad   .LBB1_8
    .quad   .LBB1_8
    .quad   .LBB1_8
    .quad   .LBB1_8
    .quad   .LBB1_5
    .quad   .LBB1_6
    .quad   .LBB1_7

И, наконец, функция null:

    .text
    .globl  null
    .align  16, 0x90
    .type   null,@function
null:                                   # @null
.Ltmp0:
    .cfi_startproc
# BB#0:
    movl    $.L.str1, %ecx
    testl   %edi, %edi
    movl    $.L.str, %eax
    cmoveq  %rcx, %rax
    ret
.Ltmp1:
    .size   null, .Ltmp1-null
.Ltmp2:
    .cfi_endproc
.Leh_func_end0:
2 голосов
/ 30 марта 2012

Большинство компиляторов компилируют их по-разному. Но это полностью зависит от компилятора и архитектуры.

0 голосов
/ 30 марта 2012

Похоже, вам просто нужно взглянуть на дизассемблер.Переключатель действительно очень интересный) Посмотрите с оптимизацией и без.Веселитесь =)

...