Если заявление против if-else, в котором быстрее?

Я спорил с другом о тех двух фрагментов. Что быстрее и почему ?

value = 5;
if (condition) {
    value = 6;
}

и:

if (condition) {
    value = 6;
} else {
    value = 5;
}

Что делать, если значение - это матрица ?

Примечание: Я знаю, что значение = условие ? 6 : 5; существует, и я ожидаю, что это будет быстрее, но это было'т вариант.

Редактировать (запрашиваемые сотрудниками, поскольку вопрос находится на удержании на данный момент):

  • пожалуйста, ответьте на рассмотрении ассемблеру x86 порожденные основных компиляторов (сказал г++, лязг++, VC с помощью MinGW) в обоих оптимизированных и не оптимизированных версий или MIPS сборки.
  • когда сборка отличаются, объяснить, почему версия работает быстрее и когда (например на "лучше, потому что нет ветвление и ветвление имеет следующую бла-бла вопрос на")
Комментарии к вопросу (14)
Решение

ТЛ;ДР: в неоптимизированном коде если, не еще, кажется, невпопад эффективнее, но даже с самым базовым уровнем включенной оптимизации кода в принципе переписать как value = условие + 5.


Я дал ему попробовать]1 и создается сборка для следующий код:

int ifonly(bool condition, int value)
{
    value = 5;
    if (condition) {
        value = 6;
    }
    return value;
}

int ifelse(bool condition, int value)
{
    if (condition) {
        value = 6;
    } else {
        value = 5;
    }
    return value;
}

На ССЗ 6.3 с оптимизацией отключена (-О0), соответствующая разница:

 mov     DWORD PTR [rbp-8], 5
 cmp     BYTE PTR [rbp-4], 0
 je      .L2
 mov     DWORD PTR [rbp-8], 6
.L2:
 mov     eax, DWORD PTR [rbp-8]

для Если бы только, а оператору ifelse имеет

 cmp     BYTE PTR [rbp-4], 0
 je      .L5
 mov     DWORD PTR [rbp-8], 6
 jmp     .L6
.L5:
 mov     DWORD PTR [rbp-8], 5
.L6:
 mov     eax, DWORD PTR [rbp-8]

Последняя выглядит чуть менее эффективен, поскольку имеет дополнительный прыжок, но оба имеют не менее двух и не более трех заданий, так что если вам действительно нужно, чтобы выжать каждую последнюю каплю производительности (подсказка: если вы работаете на космических челноках вы Don'т, и даже тогда вы probably Дон'т) разница выиграл'т быть заметно.

Однако, даже с самым низким уровнем оптимизации (-О1) обе функции сократить до той же:

test    dil, dil
setne   al
movzx   eax, al
add     eax, 5

что в принципе эквивалентно

return 5 + condition;

предполагая, что "условие" - это ноль или один. Более высокие уровни оптимизации Дон'т действительно изменить выход, если не удается избежать movzx эффективно обнуления регистра де-факто на старте.


Предупреждение: Вы, наверное,'т писать 5 + состояние себя (хотя стандарт гарантирует, что преобразование правда в целое число типа дает 1), потому что ваше намерение не может быть очевидным для людей, читающих ваш код (который может включать ваше будущее). Смысл этого кода, чтобы показать, что то, что компилятор производит в обоих случаях (практически) идентичны. Киприан Tomoiaga государства-это очень хорошо в комментариях:

а человека'ы задача написать код для людей и пусть компилятор писать код для машина.

Комментарии (19)

Ответ от CompuChip показывает, что для типа int они оба оптимизированы для одной и той же сборки, так это не'т вопрос.

что если значение матрицы ?

Я буду интерпретировать это в более общем виде, т. е. Что делать, если значение имеет тип, чья конструкций и назначений дорогие (и ходы дешево).

тогда

T value = init1;
if (condition)
   value = init2;

неоптимально, потому что в состоянии дела `это верно, вы делаете ненужные инициализацииinit1, а потом выполнить задание копирования.

T value;
if (condition)
   value = init2;
else
   value = init3;

Это лучше. Но все-таки оптимальным, если строительство по умолчанию стоит дорого и если строительство копия стоит дороже, то инициализация.

У вас есть условный оператор решение, что хорошо:

T value = condition ? init1 : init2;

Или, если вы Don'т, как условный оператор, вы можете создать вспомогательную функцию такой:

T create(bool condition)
{
  if (condition)
     return {init1};
  else
     return {init2};
}

T value = create(condition);

В зависимости от того, что init1 и init2-вы также можете рассмотреть этот:

auto final_init = condition ? init1 : init2;
T value = final_init;

Но я опять должен подчеркнуть, что это актуально только тогда, когда строительство и задания очень дорогие для данного типа. И то, только по профайлинг вы знаете наверняка.

Комментарии (8)

В псевдо-ассемблере,

в

    li    #0, r0
    test  r1
    beq   L1
    li    #1, r0
L1:

may или может not быть быстрее, чем

в

    test  r1
    beq   L1
    li    #1, r0
    bra   L2
L1:
    li    #0, r0
L2:

в зависимости от того, насколько сложным фактического процессора. Переходя от простых до модных:

  • С ЦП любой произведенных после примерно 1990, хорошая производительность зависит от сторона Код в кэш инструкций. Когда в сомнении, следовательно, уменьшить размер кода. Это весит в пользу первого примера.

  • Базовый " ив-порядке, пятиступенчатый конвейер" и процессор, который до сих пор примерно то, что вы получаете во многих микроконтроллерах есть трубопровод пузырь каждый раз, когда филиал—условное или безусловное—взято, так что это также важно свести к минимуму количество отраслевых инструкций. Это также весит в пользу первого примера.

  • Несколько более сложных процессоров—фантазии хватило, чтобы сделать себе "для исполнения", но не фантазии достаточно, чтобы использовать наиболее известных реализаций этой концепции—может понести производство пузырьков, когда они сталкиваются запись после записи hazards. Это весит в пользу пример second, где Р0 пишется только один раз несмотря ни на что. Эти процессоры обычно фантазии хватает на процесс безусловные переходы в сборщика инструкция, так что вы _aren', или просто торговле запись после записи штраф за ветку казни.

Я не'т знать, если кто-то еще делает больше такого процессора. Однако, на процессорах, которые до использования в "самым известным реализациям" из вне-порядок выполнения правоподобны для того чтобы отрезать углы на менее часто используемые инструкции, так что вы должны знать, что такая вещь может случиться. Реальный пример ложных зависимостей по данным на назначение регистров в popcnt и lzcnt на Сэнди процессоров моста.

  • На самой верхушке, ООО двигатель будет заводиться выдавать точно такую же последовательность внутренних операций для обоих фрагментов кода—это и есть аппаратная версия, что "Дон't беспокоиться об этом, компилятор будет генерировать один и тот же машинный код в любом случае.&я, однако, объем кода все-таки имеет значение, и теперь вы также должны беспокоиться о предсказуемости условного перехода. Предсказания ветвлений неспособность привести к полному трубопровода flush, который является катастрофическим для служебной деятельности; см https://stackoverflow.com/questions/11227809/why-is-it-faster-to-process-a-sorted-array-than-an-unsorted-array чтобы понять, сколько разница это сделает.

Если в ветке такое крайне непредсказуема, и ваш процессор имеет условное или условно-инструкции Move, это время, чтобы использовать их:

в

Ли #0, Р0 тест Р1 setne Р0

или

в

Ли #0, Р0 ли #1, Р2 тест Р1 мовне Р2, Р0

Условно-версия является более компактным, чем любой другой альтернативы; если это образование доступно практически гарантированно будет правильно в этом случае, даже если ветка была предсказуема. Условно-переместить версия требует дополнительного регистра нуля, и всегда отходы одного литий инструкция's стоимостью отправки и выполнения ресурсы; если бы ветка была в самом деле предсказуема, ветвистые версия вполне может быть быстрее.

Комментарии (4)

В коде unoptimised, первый пример присваивает переменной всегда сразу, а иногда и дважды. Во втором примере только один раз присваивает переменной. Условный одинаковый на обоих путях код, так что должны'т вопрос. В оптимизированный код, это зависит от компилятора.

Как всегда, если вы обеспокоены тем, что, произвести сборку и посмотреть, что компилятор на самом деле делает.

Комментарии (2)

Что бы заставить вас думать, что любой из них даже один вкладыш быстрее или медленнее?

unsigned int fun0 ( unsigned int condition, unsigned int value )
{
    value = 5;
    if (condition) {
        value = 6;
    }
    return(value);
}
unsigned int fun1 ( unsigned int condition, unsigned int value )
{

    if (condition) {
        value = 6;
    } else {
        value = 5;
    }
    return(value);
}
unsigned int fun2 ( unsigned int condition, unsigned int value )
{
    value = condition ? 6 : 5;
    return(value);
}

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

00000000 :
   0:   e3500000    cmp r0, #0
   4:   03a00005    moveq   r0, #5
   8:   13a00006    movne   r0, #6
   c:   e12fff1e    bx  lr

00000010 :
  10:   e3500000    cmp r0, #0
  14:   13a00006    movne   r0, #6
  18:   03a00005    moveq   r0, #5
  1c:   e12fff1e    bx  lr

00000020 :
  20:   e3500000    cmp r0, #0
  24:   13a00006    movne   r0, #6
  28:   03a00005    moveq   r0, #5
  2c:   e12fff1e    bx  lr

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

0000000000000000 :
   0:   7100001f    cmp w0, #0x0
   4:   1a9f07e0    cset    w0, ne
   8:   11001400    add w0, w0, #0x5
   c:   d65f03c0    ret

0000000000000010 :
  10:   7100001f    cmp w0, #0x0
  14:   1a9f07e0    cset    w0, ne
  18:   11001400    add w0, w0, #0x5
  1c:   d65f03c0    ret

0000000000000020 :
  20:   7100001f    cmp w0, #0x0
  24:   1a9f07e0    cset    w0, ne
  28:   11001400    add w0, w0, #0x5
  2c:   d65f03c0    ret

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

Насколько матрицу идет, не знаю,как это важно,

if(condition)
{
 big blob of code a
}
else
{
 big blob of code b
}

просто собираюсь поставить же если-то-иначе обертка вокруг большой капли кода они значение=5 или что-то более сложное. Также для сравнения, даже если это большой кусок кода, это все равно должно быть вычислено, и равна или не равна-то часто составлены с отрицательным, если (условие) что-то делать часто составлены как бы не Гото состоянии.

00000000 :
   0:   0f 93           tst r15     
   2:   03 24           jz  $+8         ;abs 0xa
   4:   3f 40 06 00     mov #6, r15 ;#0x0006
   8:   30 41           ret         
   a:   3f 40 05 00     mov #5, r15 ;#0x0005
   e:   30 41           ret         

00000010 :
  10:   0f 93           tst r15     
  12:   03 20           jnz $+8         ;abs 0x1a
  14:   3f 40 05 00     mov #5, r15 ;#0x0005
  18:   30 41           ret         
  1a:   3f 40 06 00     mov #6, r15 ;#0x0006
  1e:   30 41           ret         

00000020 :
  20:   0f 93           tst r15     
  22:   03 20           jnz $+8         ;abs 0x2a
  24:   3f 40 05 00     mov #5, r15 ;#0x0005
  28:   30 41           ret         
  2a:   3f 40 06 00     mov #6, r15 ;#0x0006
  2e:   30 41

мы просто прошли через это упражнение с кем-то недавно еще на сайте StackOverflow. этот компилятор для архитектуры MIPS интересно в этом случае не только осознал функций остались прежними, но была одна функция просто перейти к другому, чтобы экономить на пространстве кода. Не делай этого хоть здесь

00000000 :
   0:   0004102b    sltu    $2,$0,$4
   4:   03e00008    jr  $31
   8:   24420005    addiu   $2,$2,5

0000000c :
   c:   0004102b    sltu    $2,$0,$4
  10:   03e00008    jr  $31
  14:   24420005    addiu   $2,$2,5

00000018 :
  18:   0004102b    sltu    $2,$0,$4
  1c:   03e00008    jr  $31
  20:   24420005    addiu   $2,$2,5

больше некоторые цели.

00000000 :
   0:   1166            mov r5, -(sp)
   2:   1185            mov sp, r5
   4:   0bf5 0004       tst 4(r5)
   8:   0304            beq 12 
   a:   15c0 0006       mov $6, r0
   e:   1585            mov (sp)+, r5
  10:   0087            rts pc
  12:   15c0 0005       mov $5, r0
  16:   1585            mov (sp)+, r5
  18:   0087            rts pc

0000001a :
  1a:   1166            mov r5, -(sp)
  1c:   1185            mov sp, r5
  1e:   0bf5 0004       tst 4(r5)
  22:   0204            bne 2c 
  24:   15c0 0005       mov $5, r0
  28:   1585            mov (sp)+, r5
  2a:   0087            rts pc
  2c:   15c0 0006       mov $6, r0
  30:   1585            mov (sp)+, r5
  32:   0087            rts pc

00000034 :
  34:   1166            mov r5, -(sp)
  36:   1185            mov sp, r5
  38:   0bf5 0004       tst 4(r5)
  3c:   0204            bne 46 
  3e:   15c0 0005       mov $5, r0
  42:   1585            mov (sp)+, r5
  44:   0087            rts pc
  46:   15c0 0006       mov $6, r0
  4a:   1585            mov (sp)+, r5
  4c:   0087            rts pc

00000000 :
   0:   00a03533            snez    x10,x10
   4:   0515                    addi    x10,x10,5
   6:   8082                    ret

00000008 :
   8:   00a03533            snez    x10,x10
   c:   0515                    addi    x10,x10,5
   e:   8082                    ret

00000010 :
  10:   00a03533            snez    x10,x10
  14:   0515                    addi    x10,x10,5
  16:   8082                    ret

и компиляторы

с этим я код можно было бы ожидать различных целей в матче

define i32 @fun0(i32 %condition, i32 %value) #0 {
  %1 = icmp ne i32 %condition, 0
  %. = select i1 %1, i32 6, i32 5
  ret i32 %.
}

; Function Attrs: norecurse nounwind readnone
define i32 @fun1(i32 %condition, i32 %value) #0 {
  %1 = icmp eq i32 %condition, 0
  %. = select i1 %1, i32 5, i32 6
  ret i32 %.
}

; Function Attrs: norecurse nounwind readnone
define i32 @fun2(i32 %condition, i32 %value) #0 {
  %1 = icmp ne i32 %condition, 0
  %2 = select i1 %1, i32 6, i32 5
  ret i32 %2
}

00000000 :
   0:   e3a01005    mov r1, #5
   4:   e3500000    cmp r0, #0
   8:   13a01006    movne   r1, #6
   c:   e1a00001    mov r0, r1
  10:   e12fff1e    bx  lr

00000014 :
  14:   e3a01006    mov r1, #6
  18:   e3500000    cmp r0, #0
  1c:   03a01005    moveq   r1, #5
  20:   e1a00001    mov r0, r1
  24:   e12fff1e    bx  lr

00000028 :
  28:   e3a01005    mov r1, #5
  2c:   e3500000    cmp r0, #0
  30:   13a01006    movne   r1, #6
  34:   e1a00001    mov r0, r1
  38:   e12fff1e    bx  lr

fun0:
    push.w  r4
    mov.w   r1, r4
    mov.w   r15, r12
    mov.w   #6, r15
    cmp.w   #0, r12
    jne .LBB0_2
    mov.w   #5, r15
.LBB0_2:
    pop.w   r4
    ret

fun1:
    push.w  r4
    mov.w   r1, r4
    mov.w   r15, r12
    mov.w   #5, r15
    cmp.w   #0, r12
    jeq .LBB1_2
    mov.w   #6, r15
.LBB1_2:
    pop.w   r4
    ret

fun2:
    push.w  r4
    mov.w   r1, r4
    mov.w   r15, r12
    mov.w   #6, r15
    cmp.w   #0, r12
    jne .LBB2_2
    mov.w   #5, r15
.LBB2_2:
    pop.w   r4
    ret

Сейчас технически нет разницы в производительности в некоторых из этих решений, иногда в результате 5 чехол имеет перепрыгнуть результат 6 кода, и наоборот, ветка быстрее, чем выполнение путем? можно возразить, но исполнение должно быть различным. Но это скорее если состояние против, если бы не условие в коде, в результате чего компилятор делать если перепрыгнуть еще через выполнить. но это не обязательно из-за стиля написания кода, но в сравнении, а если и остальные случаи в любой синтаксис.

Комментарии (0)

ОК, после сборки один из тегов, я просто предполагаю, что ваш код-это псевдокод (и не обязательно C) и перевести его на человека в сборку 6502.

1-й вариант (без else)

        ldy #$00
        lda #$05
        dey
        bmi false
        lda #$06
false   brk

2-й вариант (с другой)

        ldy #$00
        dey
        bmi else
        lda #$06
        sec
        bcs end
else    lda #$05
end     brk

Предположения: состояние находится в г реестр установить это значение на 0 или 1 на первой линии в любом варианте, результат будет в аккумуляторе.

Так, после подсчета циклов для обоих вариантов в каждом конкретном случае, мы видим, что 1-ая конструкция, как правило, быстрее; 9 циклов, когда состояние 0 и 10 циклов, когда условие 1, тогда как второй вариант-также 9 циклов, когда состояние 0, а 13 циклов, когда условие 1. (цикличные подсчеты не включают в себя БРК в конце).

Вывод: если только быстрее если-то построить.

И для полноты картины, вот это оптимизированная значение = условие + решение 5:

ldy #$00
lda #$00
tya
adc #$05
brk

Это сокращает наше время до 8 циклов (опять же не включая БРК в конце).

Комментарии (3)