Я спорил с другом о тех двух фрагментов. Что быстрее и почему ?
value = 5;
if (condition) {
value = 6;
}
и:
if (condition) {
value = 6;
} else {
value = 5;
}
Что делать, если значение
- это матрица ?
Примечание: Я знаю, что значение = условие ? 6 : 5;
существует, и я ожидаю, что это будет быстрее, но это было'т вариант.
Редактировать (запрашиваемые сотрудниками, поскольку вопрос находится на удержании на данный момент):
ТЛ;ДР: в неоптимизированном коде если
, не еще
, кажется, невпопад эффективнее, но даже с самым базовым уровнем включенной оптимизации кода в принципе переписать как 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 государства-это очень хорошо в комментариях:
а человека'ы задача написать код для людей и пусть компилятор писать код для машина.
Ответ от 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;
Но я опять должен подчеркнуть, что это актуально только тогда, когда строительство и задания очень дорогие для данного типа. И то, только по профайлинг вы знаете наверняка.
В псевдо-ассемблере,
в
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 на Сэнди процессоров моста.
Если в ветке такое крайне непредсказуема, и ваш процессор имеет условное или условно-инструкции Move, это время, чтобы использовать их:
в
Ли #0, Р0 тест Р1 setne Р0
или
в
Ли #0, Р0 ли #1, Р2 тест Р1 мовне Р2, Р0
Условно-версия является более компактным, чем любой другой альтернативы; если это образование доступно практически гарантированно будет правильно в этом случае, даже если ветка была предсказуема. Условно-переместить версия требует дополнительного регистра нуля, и всегда отходы одного литий
инструкция's стоимостью отправки и выполнения ресурсы; если бы ветка была в самом деле предсказуема, ветвистые версия вполне может быть быстрее.
В коде unoptimised, первый пример присваивает переменной всегда сразу, а иногда и дважды. Во втором примере только один раз присваивает переменной. Условный одинаковый на обоих путях код, так что должны'т вопрос. В оптимизированный код, это зависит от компилятора.
Как всегда, если вы обеспокоены тем, что, произвести сборку и посмотреть, что компилятор на самом деле делает.
Что бы заставить вас думать, что любой из них даже один вкладыш быстрее или медленнее?
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 <fun0>:
0: e3500000 cmp r0, #0
4: 03a00005 moveq r0, #5
8: 13a00006 movne r0, #6
c: e12fff1e bx lr
00000010 <fun1>:
10: e3500000 cmp r0, #0
14: 13a00006 movne r0, #6
18: 03a00005 moveq r0, #5
1c: e12fff1e bx lr
00000020 <fun2>:
20: e3500000 cmp r0, #0
24: 13a00006 movne r0, #6
28: 03a00005 moveq r0, #5
2c: e12fff1e bx lr
не большой сюрприз в первый функционировать в разном порядке, если же время выполнения.
0000000000000000 <fun0>:
0: 7100001f cmp w0, #0x0
4: 1a9f07e0 cset w0, ne
8: 11001400 add w0, w0, #0x5
c: d65f03c0 ret
0000000000000010 <fun1>:
10: 7100001f cmp w0, #0x0
14: 1a9f07e0 cset w0, ne
18: 11001400 add w0, w0, #0x5
1c: d65f03c0 ret
0000000000000020 <fun2>:
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 <fun0>:
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 <fun1>:
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 <fun2>:
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 <fun0>:
0: 0004102b sltu $2,$0,$4
4: 03e00008 jr $31
8: 24420005 addiu $2,$2,5
0000000c <fun1>:
c: 0004102b sltu $2,$0,$4
10: 03e00008 jr $31
14: 24420005 addiu $2,$2,5
00000018 <fun2>:
18: 0004102b sltu $2,$0,$4
1c: 03e00008 jr $31
20: 24420005 addiu $2,$2,5
больше некоторые цели.
00000000 <_fun0>:
0: 1166 mov r5, -(sp)
2: 1185 mov sp, r5
4: 0bf5 0004 tst 4(r5)
8: 0304 beq 12 <_fun0+0x12>
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 <_fun1>:
1a: 1166 mov r5, -(sp)
1c: 1185 mov sp, r5
1e: 0bf5 0004 tst 4(r5)
22: 0204 bne 2c <_fun1+0x12>
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 <_fun2>:
34: 1166 mov r5, -(sp)
36: 1185 mov sp, r5
38: 0bf5 0004 tst 4(r5)
3c: 0204 bne 46 <_fun2+0x12>
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 <fun0>:
0: 00a03533 snez x10,x10
4: 0515 addi x10,x10,5
6: 8082 ret
00000008 <fun1>:
8: 00a03533 snez x10,x10
c: 0515 addi x10,x10,5
e: 8082 ret
00000010 <fun2>:
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 <fun0>:
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 <fun1>:
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 <fun2>:
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 кода, и наоборот, ветка быстрее, чем выполнение путем? можно возразить, но исполнение должно быть различным. Но это скорее если состояние против, если бы не условие в коде, в результате чего компилятор делать если перепрыгнуть еще через выполнить. но это не обязательно из-за стиля написания кода, но в сравнении, а если и остальные случаи в любой синтаксис.
ОК, после сборки один из тегов, я просто предполагаю, что ваш код-это псевдокод (и не обязательно 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 циклов (опять же не включая БРК
в конце).