if 문과 if-else 문 중 어느 것이 더 빠릅니까? [닫은]


84

나는 요 전에 그 두 조각에 대해 친구와 논쟁했습니다. 어느 것이 더 빠르고 왜?

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

과:

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

만약에 value매트릭스는?

참고 : 그게 value = condition ? 6 : 5;존재 한다는 것을 알고 있고 더 빠를 것으로 기대하지만 옵션이 아닙니다.

수정 (현재 질문이 보류 중이므로 직원이 요청 함) :

  • 최적화 된 버전과 최적화되지 않은 버전 모두에서 주류 컴파일러 ( 예 : g ++, clang ++, vc, mingw )에 의해 생성 된 x86 어셈블리 또는 MIPS 어셈블리 를 고려하여 답변하십시오 .
  • 어셈블리가 다를 때 버전이 더 빠른 이유와시기를 설명하십시오 ( 예 : "Branching 및 분기에 다음 문제가 발생하지 않기 때문에 더 좋음" ).

173
최적화는 죽일 모든 그것은 중요하지 않습니다에서 ...
양자 물리학

21
프로필을 작성할 수 있습니다. 개인적으로 현대 컴파일러를 사용하면 어떤 차이가 있는지 의심 스럽습니다.
George

25
value = condition ? 6 : 5;대신 사용 if/else하면 동일한 코드가 생성 될 가능성이 큽니다. 더 많은 것을 알고 싶다면 어셈블리 출력을보십시오.
Jabberwocky

8
이 경우에 가장 중요한 것은 여기서 가장 비싼 지점을 피하는 것입니다. (파이프 리로드가 폐기 프리 페치 지시 등)
Tommylee2k

11
이와 같이 속도를 위해 마이크로 최적화하는 것이 합리적 일 때는 여러 번 실행되는 루프 내부에 있으며,이 사소한 예제에서 gcc가 할 수있는 것처럼 옵티마이 저가 모든 분기 명령을 최적화하거나 실제 세계에서 할 수 있습니다. 성능은 올바른 분기 예측에 크게 좌우됩니다 ( stackoverflow.com/questions/11227809/…에 대한 필수 링크 ). 불가피하게 루프 내에서 분기하는 경우 프로필을 생성하고이를 다시 컴파일하여 분기 예측자를 도울 수 있습니다.
Davislor

답변:


282

요약 : 최적화되지 않은 코드에서 if없이는 else관련이 없을 정도로 더 효율적으로 보이지만 가장 기본적인 수준의 최적화를 사용하면 코드가 기본적으로 value = condition + 5.


나는 그것을 시도 하고 다음 코드에 대한 어셈블리를 생성했습니다.

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;
}

최적화가 비활성화 된 gcc 6.3 ( -O0)에서 관련 차이점은 다음과 같습니다.

 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]

에 대한 ifonly반면이 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]

후자는 추가 점프가 있기 때문에 약간 덜 효율적으로 보이지만 둘 다 최소 2 개와 최대 3 개의 할당이 있으므로 마지막 성능 저하를 모두 짜낼 필요가없는 한 (힌트 : 우주 왕복선에서 작업하지 않는 한 , 그리고 당신은 아마 그 차이가 눈에 띄지 않을 것입니다.

그러나 가장 낮은 최적화 수준 ( -O1)을 사용 하더라도 두 함수는 모두 동일하게 감소합니다.

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

기본적으로 다음과 같습니다.

return 5 + condition;

condition0 또는 1 이라고 가정합니다 . 더 높은 최적화 수준은 처음에 레지스터를 movzx효율적으로 제로화 하여이를 방지하는 것을 제외하고는 실제로 출력을 변경하지 않습니다 EAX.


면책 조항 : 당신 의 의도가 당신의 코드를 읽는 사람들에게 즉각적으로 명백하지 않을 수 있기 때문에 ( 정수 유형으로의 5 + condition변환이 제공한다고 표준이 보장하더라도) 당신은 당신 자신을 작성해서는 안됩니다 . 이 코드의 요점은 컴파일러가 두 경우 모두에서 생성하는 것이 (실제적으로) 동일하다는 것을 보여주는 것입니다. Ciprian Tomoiaga 는 의견에서이를 아주 잘 설명합니다.true1

인간 의 작업은 코드를 작성하는 것입니다 인간에 대한 그리고하자 컴파일러 에 대한 코드를 작성 기계를 .


50
이것은 훌륭한 답변이며 받아 들여 져야합니다.
dtell

10
나는 결코 불구하고 추가를 사용했습니다 것이다 (<- 파이썬은 당신에게 무엇을.)
치프 리안 Tomoiagă

26
@CiprianTomoiaga 및 옵티 마이저를 작성하지 않는 한 그렇게해서는 안됩니다! 거의 모든 경우에 컴파일러가 이와 같은 최적화를 수행하도록해야합니다. 특히 코드 가독성을 크게 떨어 뜨리는 경우에는 더욱 그렇습니다. 성능 테스트에서 특정 코드에 문제가있는 경우에만이를 최적화하려고 시도한 다음 깔끔하고 잘 설명 된 상태로 유지하고 측정 가능한 차이를 만드는 최적화 만 수행해야합니다.
Muzer 2017-04-04

22
Muzer에 답장하고 싶었지만 스레드에 아무것도 추가하지 않았을 것입니다. 그러나 저는 인간 의 임무가 인간을 위한 코드를 작성 하고 컴파일러 가 기계를 위한 코드 를 작성하도록하는 것임을 다시 말하고 싶습니다 . 나는 컴파일러 개발자 PoV (나는 아니지만 그것에 대해 조금 배웠습니다)
말했습니다

10
true로 변환 된 값 은 int항상 1, period를 산출합니다. 물론, 당신의 상태가 bool가치 가 아니라 단순히 "진정한" true것이라면 그것은 완전히 다른 문제입니다.
TC

44

CompuChip 의 답변은 int둘 다 동일한 어셈블리에 최적화되어 있으므로 중요하지 않다는 것을 보여줍니다 .

값이 행렬이면 어떨까요?

나는 이것을 좀 더 일반적인 방식으로 해석 할 것입니다. 즉 value, 구성과 할당이 비싸고 이동이 저렴한 유형 이라면 어떨까요?

그때

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

conditiontrue 인 경우 불필요한 초기화를 init1수행 한 다음 복사 할당을 수행 하기 때문에 차선책 입니다.

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

이게 낫다. 그러나 기본 구성이 비싸고 복사 구성이 초기화보다 더 비싸다면 여전히 차선책입니다.

좋은 조건부 연산자 솔루션이 있습니다.

T value = condition ? init1 : init2;

또는 조건부 연산자가 마음에 들지 않으면 다음과 같은 도우미 함수를 만들 수 있습니다.

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;

그러나 나는 이것이 주어진 유형에 대해 건설과 할당이 정말로 비쌀 때만 관련이 있음을 강조해야합니다. 그런 다음에도 프로파일 링 을 통해서만 확실히 알 수 있습니다.


3
비싼 하고 있지 밖으로 최적화. 예를 들어, 기본 생성자가 행렬을 0으로 설정하면 컴파일러는 할당이 0을 덮어 쓰는 것이므로 0이 아니라이 메모리에 직접 쓴다는 것을 인식 할 수 있습니다. 그들이 ... 또는하지 찰 때 예측하기 어려운, 그래서 물론, 최적화는 몹시 신경을 쓰는 짐승은
매튜 M.

@MatthieuM. 물론이야. "비싸다"는 것은 컴파일러 최적화 후에도 "실행 비용이 비쌉니다 (메트릭에 따라 CPU 클럭, 리소스 사용률 등).
bolov

기본 건설은 비싸지 만 저렴하게 움직일 것 같지 않습니다.
plugwash 2017-04-04

6
@plugwash 매우 큰 할당 된 배열이있는 클래스를 고려하십시오. 기본 생성자는 비용이 많이 드는 배열을 할당하고 초기화합니다. 이동 (복사 아님!) 생성자는 소스 객체와 포인터를 교체 할 수 있으며 큰 배열을 할당하거나 초기화 할 필요가 없습니다.
TrentP

1
부품이 단순하다면 ?:새로운 기능을 도입하는 것보다 연산자를 사용하는 것이 더 좋습니다. 결국 조건을 함수에 전달할뿐만 아니라 생성자 인수도 전달할 수 있습니다. 일부 create()는 조건 에 따라 사용하지 못할 수도 있습니다 .
cmaster-monica 복원

12

의사 어셈블리 언어에서

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

더 빠르 거나 빠르지 않을 수도 있습니다.

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

실제 CPU가 얼마나 정교한 지에 따라 다릅니다. 가장 단순한 것에서 가장 공상적인 것까지 :

  • 으로 어떤 CPU가 대략 1990 이후에 제조 된, 좋은 성능은 내 피팅 코드에 따라 명령어 캐시 . 따라서 의심스러운 경우 코드 크기를 최소화하십시오. 이것은 첫 번째 예에 유리합니다.

  • 여전히 많은 마이크로 컨트롤러에서 얻을 수 있는 기본 " 순서대로, 5 단계 파이프 라인 "CPU를 사용 하면 분기 (조건부 또는 무조건 부)를 취할 때마다 파이프 라인 버블 이 발생하므로 최소화하는 것도 중요합니다. 분기 명령의 수. 이것은 또한 첫 번째 예에 유리합니다.

  • 다소 더 정교한 CPU ( " 순서에 맞지 않는 실행 "을 수행 할 수있을만큼 멋지지만 해당 개념의 가장 잘 알려진 구현을 사용할만큼 멋지지 않음)는 쓰기 후 쓰기 위험이 발생할 때마다 파이프 라인 버블을 일으킬 수 있습니다 . 이것은 두 번째 예 에 유리하며, r0어떤 경우에도 한 번만 작성됩니다. 이러한 CPU는 일반적으로 명령어 페처에서 무조건 분기를 처리 할 수있을만큼 멋지므로 쓰기 후 쓰기 패널티를 분기 패널티로 거래하는 것이 아닙니다 .

    더 이상 이런 종류의 CPU를 만드는 사람이 있는지 모르겠습니다. 그러나, CPU를 않습니다 당신이 이런 종류의 일이 일어날 수 있다는 것을 인식 할 필요가 있으므로 아웃 오브 오더 실행의 "가장 잘 알려진 구현"을 사용은 자주 사용 지침에 잘라 모서리 가능성이있다. 실제 예는 의 대상 레지스터에 잘못된 데이터 종속성 popcntlzcnt샌디 브릿지 CPU에서 .

  • 가장 높은 수준에서 OOO 엔진은 두 코드 조각에 대해 정확히 동일한 순서의 내부 작업을 실행합니다. 이것은 "걱정하지 마십시오. 컴파일러가 어느 쪽이든 동일한 기계 코드를 생성 할 것"의 하드웨어 버전입니다. 그러나 코드 크기는 여전히 중요하며 이제 조건부 분기의 예측 가능성에 대해서도 걱정해야합니다. 분기 예측 실패는 잠재적으로 전체 파이프 라인 플러시를 유발하여 성능에 치명적입니다. 정렬되지 않은 배열보다 정렬 된 배열을 처리하는 것이 더 빠른 이유를 참조하십시오 . 이것이 얼마나 많은 차이를 만들 수 있는지 이해합니다.

    분기 매우 예측 불가능하고 CPU에 조건부 설정 또는 조건부 이동 명령이있는 경우 다음과 같이 사용할 시간입니다.

        li    #0, r0
        test  r1
        setne r0
    

    또는

        li    #0, r0
        li    #1, r2
        test  r1
        movne r2, r0
    

    조건부 설정 버전은 다른 대안보다 더 간결합니다. 해당 명령을 사용할 수있는 경우 분기가 예측 가능하더라도이 시나리오에 대한 올바른 것이 실제로 보장됩니다. 조건부 이동 버전에는 추가 스크래치 레지스터가 필요하며 항상 하나의 li명령어 분량의 디스패치 및 실행 리소스를 낭비 합니다. 분기가 실제로 예측 가능한 경우 분기 버전이 더 빠를 수 있습니다.


CPU에 쓰기 후 쓰기 위험으로 인해 지연되는 비 순차적 엔진이 있는지 여부에 대해 두 번째 요점을 다시 말하겠습니다. 는 CPU 지체없이 이러한 위험을 처리 할 수있는 순서가 잘못된 엔진이있는 경우에는 문제가 없지만 CPU가 아웃 오브 오더 엔진이없는 경우에는 문제가 없다 전혀를 .
supercat apr.

@supercat 마지막에있는 단락은 그 경우를 다루기위한 것이지만 더 명확하게 만드는 방법에 대해 생각할 것입니다.
zwol 2017-04-04

현재 CPU에 캐시가있어 순차적으로 실행되는 코드가 첫 번째보다 두 번째로 더 빠르게 실행될 수 있는지 모르겠습니다 (일부 플래시 기반 ARM 부품에는 몇 행의 플래시 데이터를 버퍼링 할 수있는 인터페이스가 있지만 코드를 실행하는만큼 빠르게 순차적으로 가져올 수 있지만 분기가 많은 코드를 빠르게 실행하는 핵심은이를 RAM에 복사하는 것입니다. 비 순차적 실행이 전혀없는 CPU는 쓰기 후 쓰기 위험으로 인해 지연되는 CPU보다 훨씬 더 일반적입니다.
supercat

이것은 매우 통찰력
Julien__

9

최적화되지 않은 코드에서 첫 번째 예제는 항상 한 번, 때로는 두 번 변수를 할당합니다. 두 번째 예는 변수를 한 번만 할당합니다. 조건부는 두 코드 경로에서 동일하므로 중요하지 않습니다. 최적화 된 코드에서는 컴파일러에 따라 다릅니다.

항상 그렇듯이, 그럴 경우 어셈블리를 생성하고 컴파일러가 실제로 수행하는 작업을 확인하십시오.


1
성능이 염려되면 최적화되지 않은 상태로 컴파일되지 않습니다. 그러나 확실히 최적화 프로그램이 얼마나 좋은지는 컴파일러 / 버전에 따라 다릅니다.
old_timer

AFAIK에는 컴파일러 / CPU 아키텍처 등에 대한 설명이 없으므로 잠재적으로 컴파일러가 최적화를 수행하지 않습니다. 8 비트 PIC에서 64 비트 Xeon에 이르기까지 무엇이든 컴파일 할 수 있습니다.
Neil

8

하나의 라이너가 더 빠르거나 느리다고 생각하는 이유는 무엇입니까?

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
}

코드의 큰 덩어리 주위에 동일한 if-then-else 래퍼를 넣을 것입니다. value = 5 또는 더 복잡한 것입니다. 마찬가지로 코드의 큰 덩어리 라 할지라도 비교는 여전히 계산되어야하며, 어떤 것과 같거나 같지 않은 것은 종종 부정으로 컴파일됩니다. if (condition) do something은 종종 condition goto가 아닌 것처럼 컴파일됩니다.

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에서 다른 사람과 함께이 연습을 진행했습니다. 이 밉 컴파일러는 흥미롭게도 함수가 동일하다는 것을 깨달았을뿐만 아니라 코드 공간을 절약하기 위해 한 함수가 다른 함수로 점프했습니다. 그래도 여기에서하지 않았다

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 개 코드를 넘어 가고 그 반대의 경우 분기가 실행하는 것보다 빠릅니다. 논쟁 할 수는 있지만 실행은 다양해야합니다. 그러나 그것은 컴파일러가 if this jump over else 실행을 수행하는 코드의 if 조건 대 그렇지 않은 조건에 더 가깝습니다. 그러나 이것은 반드시 코딩 스타일 때문이 아니라 어떤 구문의 비교와 if 및 else 경우 때문입니다.


0

좋아, 어셈블리는 태그 중 하나이므로 코드가 의사 코드라고 가정하고 (반드시 c는 아님) 사람이 6502 어셈블리로 변환합니다.

첫 번째 옵션 (다른 옵션 없음)

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

두 번째 옵션 (다른 옵션 포함)

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

가정 : 조건이 Y 레지스터에 있습니다. 두 옵션의 첫 번째 줄에서 0 또는 1로 설정하면 결과가 누산기에 나타납니다.

따라서 각 경우의 두 가능성에 대한주기를 계산 한 후 첫 번째 구성이 일반적으로 더 빠르다는 것을 알 수 있습니다. 조건이 0 일 때 9 사이클, 조건이 1 일 때 10 사이클, 반면 옵션 2는 조건이 0 일 때 9 사이클이지만 조건이 1 일 때 13 사이클입니다 ( 사이클 카운트는 BRK끝에 있는 것을 포함하지 않음 ).

결론 : 구성 If only보다 빠릅니다 If-Else.

완성도를 위해 최적화 된 value = condition + 5솔루션은 다음과 같습니다.

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

이것은 우리의 시간을 8 사이클로 줄입니다 ( 다시 BRK끝을 포함하지 않음 ).


6
이 답변에 대해 안타깝게도 동일한 소스 코드 를 C 컴파일러 (또는 C ++ 컴파일러)에 입력하면 Glen의 두뇌에 입력하는 것과는 매우 다른 출력이 생성됩니다. 소스 코드 수준에서 대안간에 차이가없고 "최적화"가능성이 없습니다. 가장 읽기 쉬운 것을 사용하십시오 (아마도 if / else).
Quuxplusone 2017

1
@네. 컴파일러는 변형을 가장 빠른 버전 으로 최적화하거나 사이의 차이를 훨씬 능가하는 추가 오버 헤드를 추가 할 가능성이 있습니다. 아니면 둘다.
jpaugh

1
질문이 C ++ (불행히도 관련된 변수 의 유형 을 선언하지 못함)로 태그가 지정 되었기 때문에 " 반드시 C "는 아니라고 가정하는 것이 현명한 선택으로 보입니다 .
Toby Speight
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.