Подтвердить что ты не робот

Разница в производительности "if if" vs "if else if"

Я просто думал, есть ли разница в производительности между двумя операторами в C/С++:

Случай 1:

if (p==0)
   do_this();
else if (p==1)
   do_that();
else if (p==2)
   do_these():

Случай 2:

if(p==0)
    do_this();
if(p==1)
    do_that();
if(p==2)
    do_these();
4b9b3361

Ответ 1

Предполагая простые типы (в этом случае я использовал int) и не смешной бизнес (не переопределил оператор = для int), по крайней мере с GCC 4.6 на AMD64, нет никакой разницы. Сгенерированный код идентичен:

0000000000000000 <case_1>:                                   0000000000000040 <case_2>:
   0:   85 ff                   test   %edi,%edi               40:   85 ff                   test   %edi,%edi
   2:   74 14                   je     18 <case_1+0x18>        42:   74 14                   je     58 <case_2+0x18>
   4:   83 ff 01                cmp    $0x1,%edi               44:   83 ff 01                cmp    $0x1,%edi
   7:   74 27                   je     30 <case_1+0x30>        47:   74 27                   je     70 <case_2+0x30>
   9:   83 ff 02                cmp    $0x2,%edi               49:   83 ff 02                cmp    $0x2,%edi
   c:   74 12                   je     20 <case_1+0x20>        4c:   74 12                   je     60 <case_2+0x20>
   e:   66 90                   xchg   %ax,%ax                 4e:   66 90                   xchg   %ax,%ax
  10:   f3 c3                   repz retq                      50:   f3 c3                   repz retq 
  12:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)        52:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
  18:   31 c0                   xor    %eax,%eax               58:   31 c0                   xor    %eax,%eax
  1a:   e9 00 00 00 00          jmpq   1f <case_1+0x1f>        5a:   e9 00 00 00 00          jmpq   5f <case_2+0x1f>
  1f:   90                      nop                            5f:   90                      nop
  20:   31 c0                   xor    %eax,%eax               60:   31 c0                   xor    %eax,%eax
  22:   e9 00 00 00 00          jmpq   27 <case_1+0x27>        62:   e9 00 00 00 00          jmpq   67 <case_2+0x27>
  27:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)        67:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
  2e:   00 00                                                  6e:   00 00 
  30:   31 c0                   xor    %eax,%eax               70:   31 c0                   xor    %eax,%eax
  32:   e9 00 00 00 00          jmpq   37 <case_1+0x37>        72:   e9 00 00 00 00          jmpq   77 <case_2+0x37>
  37:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
  3e:   00 00 

Дополнительная инструкция в конце case_1 предназначена только для padding (для выравнивания следующей функции).

Это не удивительно, выяснив, что p не изменяется в этой функции, это довольно простая оптимизация. Если p может быть изменен (например, передан по ссылке или указатель на различные функции do_… или был ссылкой или указателем, так что может быть псевдоним), то поведение отличается, и, конечно, сгенерированный код тоже будет.

Ответ 2

В первом случае условия после согласования не оцениваются.

Ответ 3

, если else быстрее ; если совпадение было найдено до последнего, если хотя бы последний оператор if был пропущен, если совпадение было найдено вначале, оно пропустит все остальные утверждения.

, если < медленнее; даже если совпадение было найдено с использованием первого оператора if, оно будет продолжать пытаться соответствовать в другом выражении.

Ответ 4

Да, разница в производительности:

Второй оператор оценивает каждый IF

Ответ 5

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

Обратите внимание, что при проверке int для равенства вы также можете просто использовать оператор switch. Таким образом, вы можете сохранить некоторый уровень удобочитаемости для длительной последовательности проверок.

switch ( p )
{
    case 0:
        do_this();
        break;

    case 1:
        do_that();
        break;

    case 2:
        do_these():
        break;
}

Ответ 6

Как уже было продемонстрировано... он меняется.

Если мы говорим о примитивных (встроенных) типах, таких как int, тогда компилятор может быть достаточно умным, чтобы это не имело значения (или нет). В любом случае влияние производительности будет незначительным, потому что стоимость вызова функции намного выше, чем стоимость if, поэтому разница, вероятно, потеряется в шуме, если вы попытаетесь ее измерить.

Семантика, однако, совсем другая.

Когда я прочитал первый случай:

if (...) {
  // Branch 1
} else if (...) {
  // Branch 2
}

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

Однако, когда я читаю второй случай:

if (...) {
}
if (...) {
}

Тогда мне нужно задаться вопросом, есть ли возможность взять обе ветки или нет, что означает, что я должен тщательно изучить код в первом, чтобы определить, может ли он повлиять на второй тест или нет. И когда я, наконец, не заканчиваю это, я проклинаю кровавого разработчика, который слишком ленив, чтобы написать эту чертову else, которая спасла бы меня последние 10 минут проверки.

Итак, помогите себе и вашим будущим сопровождающим, и сосредоточьтесь на правильном и понятном семантике.

И по этому вопросу можно утверждать, что, возможно, эта логика отправки может быть лучше выражена другими конструкциями, такими как switch или, возможно, map<int, void()>? (остерегайтесь последних и избегайте чрезмерной инженерии;))

Ответ 7

Основное отличие заключается в том, что конструкция if/else перестанет оценивать ifs(), когда один из них вернет true. Это означает, что МОЖЕТ выполнять только 1 или 2 из ifs перед тем, как выполнить поручение. Другая версия проверит все 3 ifs, независимо от результата других.

Итак, если /else имеет операционную стоимость "До 3 проверок". Версия if/if/if имеет операционную стоимость "всегда делает 3 проверки". Предполагая, что все три проверенных значения одинаково вероятны, версия if/else будет иметь среднее значение 1,5 ifs, а if/if всегда будет делать 3 ifs. В долгосрочной перспективе вы сэкономите 1,5% от времени процессора с конструкцией "else".

Ответ 8

Это зависит от того, как настроена пропозициональная логика кода. Насколько я понимаю, оператор "if if if" будет выполнять каждый случай, тогда как "if elif else" будет продолжать оценивать каждый случай, пока не найдет совпадение. Следовательно, если ваша логика высказываний выдает взаимоисключающий ответ, тогда "if elif else" будет выполняться быстрее, чем выполнение оператора "if if if". Я рад услышать в ответ на любого, кто хотел бы добавить/исправить это.