<가 <=보다 빠릅니까? a < 901 )보다

if( a < 901 )보다 더 빨리 if( a <= 900 ).

이 간단한 예제와 정확히 같지는 않지만 루프 복잡한 코드에서 약간의 성능 변화가 있습니다. 나는 이것이 사실 일 경우에 생성 된 머신 코드로 무언가를해야한다고 생각한다.



답변

아니요, 대부분의 아키텍처에서 더 빠르지는 않습니다. 지정하지 않았지만 x86에서 모든 적분 비교는 일반적으로 두 개의 기계 명령어로 구현됩니다.

  • test또는 cmp명령어 세트EFLAGS
  • 그리고 비교 유형 및 코드 레이아웃에 따라 Jcc(점프) 명령 :
    • jne -같지 않으면 점프-> ZF = 0
    • jz -0이면 점프 (같음)-> ZF = 1
    • jg -더 큰 경우 점프-> ZF = 0 and SF = OF
    • (기타…)

예제 (간결하게 편집)$ gcc -m32 -S -masm=intel test.c

    if (a < b) {
        // Do something 1
    }

컴파일 :

    mov     eax, DWORD PTR [esp+24]      ; a
    cmp     eax, DWORD PTR [esp+28]      ; b
    jge     .L2                          ; jump if a is >= b
    ; Do something 1
.L2:

    if (a <= b) {
        // Do something 2
    }

컴파일 :

    mov     eax, DWORD PTR [esp+24]      ; a
    cmp     eax, DWORD PTR [esp+28]      ; b
    jg      .L5                          ; jump if a is > b
    ; Do something 2
.L5:

따라서 둘 사이의 유일한 차이점은 명령 jgjge명령입니다. 두 사람은 같은 시간이 걸립니다.


다른 점프 명령이 같은 시간이 걸린다는 것을 나타내는 것은 아무것도 없습니다. 이것은 대답하기가 약간 까다 롭지 만 여기에 내가 줄 수있는 것이 있습니다 .Intel Instruction Set Reference 에서는 모두 하나의 공통 명령 Jcc(모두 조건이 충족되면 점프)으로 그룹화됩니다 . 부록 C. 지연 시간 및 처리량 의 최적화 참조 매뉴얼에 동일한 그룹화가 함께 이루어집니다 .

지연 시간 — 실행 코어가 명령을 구성하는 모든 μops의 실행을 완료하는 데 필요한 클럭주기 수입니다.

처리량 — 문제 포트가 동일한 명령을 다시 허용하기 전에 대기하는 데 필요한 클럭주기 수입니다. 많은 명령어의 경우 명령어의 처리량이 지연 시간보다 훨씬 적을 수 있습니다.

Jcc은 다음과 같습니다.

      Latency   Throughput
Jcc     N/A        0.5

다음 각주와 함께 Jcc:

7) 조건부 점프 명령의 선택은 분기의 예측 성을 향상시키기 위해 3.4.1 절.“지점 예측 최적화”섹션의 권장 사항을 기반으로해야합니다. 분기가 성공적으로 예측되면 대기 시간 jcc은 사실상 0입니다.

따라서 인텔 문서에서는 Jcc어떤 명령도 다른 명령과 다르게 처리되지 않습니다 .

명령어를 구현하기 위해 사용 된 실제 회로에 대해 생각한다면 EFLAGS, 조건이 충족되는지 여부를 결정하기 위해 서로 다른 비트에 간단한 AND / OR 게이트가 있다고 가정 할 수 있습니다 . 따라서 2 비트를 테스트하는 명령어가 1 개만 테스트하는 것보다 더 많거나 적은 시간이 걸리는 이유는 없습니다 (클럭주기보다 훨씬 짧은 게이트 전파 지연 무시).


편집 : 부동 소수점

이것은 x87 부동 소수점에도 적용됩니다. (위와 거의 동일하지만 double대신 int.

        fld     QWORD PTR [esp+32]
        fld     QWORD PTR [esp+40]
        fucomip st, st(1)              ; Compare ST(0) and ST(1), and set CF, PF, ZF in EFLAGS
        fstp    st(0)
        seta    al                     ; Set al if above (CF=0 and ZF=0).
        test    al, al
        je      .L2
        ; Do something 1
.L2:

        fld     QWORD PTR [esp+32]
        fld     QWORD PTR [esp+40]
        fucomip st, st(1)              ; (same thing as above)
        fstp    st(0)
        setae   al                     ; Set al if above or equal (CF=0).
        test    al, al
        je      .L5
        ; Do something 2
.L5:
        leave
        ret


답변

역사적으로 (우리는 1980 년대와 1990 년대 초반을 얘기하고), 거기 몇몇 이 사실있는 아키텍처. 근본적인 문제는 정수 비교가 본질적으로 정수 빼기를 통해 구현된다는 것입니다. 이로 인해 다음과 같은 경우가 발생합니다.

Comparison     Subtraction
----------     -----------
A < B      --> A - B < 0
A = B      --> A - B = 0
A > B      --> A - B > 0

이제 A < B뺄셈이 고 비트를 빌려야 뺄 때 , 뺄셈이 정확 해지려면, 손으로 더하거나 뺄 때와 같이 빌리십시오. 이 “빌려온”비트는 일반적으로 캐리 비트 라고 하며 분기 명령으로 테스트 할 수 있습니다. 빼기가 같음을 의미하는 동일 제로인 경우 0 비트라는 두 번째 비트 가 설정됩니다.

일반적으로 최소 두 개의 조건부 분기 명령어가있었습니다. 하나는 캐리 비트에서 분기하고 다른 하나는 0 비트에서 분기했습니다.

이제 문제의 핵심을 파악하기 위해 캐리 및 제로 비트 결과를 포함하도록 이전 표를 확장 해 보겠습니다.

Comparison     Subtraction  Carry Bit  Zero Bit
----------     -----------  ---------  --------
A < B      --> A - B < 0    0          0
A = B      --> A - B = 0    1          1
A > B      --> A - B > 0    1          0

따라서이 경우 에만A < B 캐리 비트가 명확하기 때문에 분기에 대한 구현은 하나의 명령으로 수행 할 수 있습니다 .

;; Implementation of "if (A < B) goto address;"
cmp  A, B          ;; compare A to B
bcz  address       ;; Branch if Carry is Zero to the new address

그러나 동등하지 않은 비교를 원한다면 평등의 경우를 잡기 위해 제로 플래그를 추가로 확인해야합니다.

;; Implementation of "if (A <= B) goto address;"
cmp A, B           ;; compare A to B
bcz address        ;; branch if A < B
bzs address        ;; also, Branch if the Zero bit is Set

따라서 일부 기계에서는 “보다 작음”비교를 사용 하면 하나의 기계 명령 저장 될 수 있습니다 . 이는 메가 헤르츠 미만 프로세서 속도와 1 : 1 CPU-to-memory 속도 비율과 관련이 있었지만 오늘날에는 거의 관련이 없습니다.


답변

내부 정수 유형에 대해 이야기하고 있다고 가정하면 하나가 다른 정수보다 빠를 수있는 방법은 없습니다. 그것들은 의미 상 분명히 동일합니다. 둘 다 컴파일러에게 정확히 동일한 작업을 수행하도록 요청합니다. 끔찍하게 고장난 컴파일러 만이 이들 중 하나에 대해 열등한 코드를 생성합니다.

일부 플랫폼 있었다면 <보다 더 빨리이었다 <=간단한 정수 유형의 컴파일러한다 항상 변환 <=<상수. 나쁜 컴파일러가 아닌 모든 컴파일러 (플랫폼 용).


답변

둘 다 빠르지 않다는 것을 알았습니다. 컴파일러는 각 조건에서 다른 값으로 동일한 기계 코드를 생성합니다.

if(a < 901)
cmpl  $900, -4(%rbp)
jg .L2

if(a <=901)
cmpl  $901, -4(%rbp)
jg .L3

내 예 if는 Linux의 x86_64 플랫폼에있는 GCC입니다.

컴파일러 작가는 꽤 똑똑한 사람들이며, 이러한 것들과 우리 대부분의 다른 사람들이 당연하다고 생각합니다.

상수가 아닌 경우 두 경우 모두 동일한 머신 코드가 생성됩니다.

int b;
if(a < b)
cmpl  -4(%rbp), %eax
jge   .L2

if(a <=b)
cmpl  -4(%rbp), %eax
jg .L3


답변

부동 소수점 코드의 경우 <= 비교는 현대 아키텍처에서도 실제로 (한 명령으로) 느려질 수 있습니다. 첫 번째 기능은 다음과 같습니다.

int compare_strict(double a, double b) { return a < b; }

PowerPC에서 먼저 부동 소수점 비교 ( cr조건 레지스터 업데이트 )를 수행 한 다음 조건 레지스터를 GPR로 이동하고 “보다 작은 비교”비트를 제자리로 이동 한 다음 리턴합니다. 네 가지 지침이 필요합니다.

이제이 기능을 대신 고려하십시오.

int compare_loose(double a, double b) { return a <= b; }

이를 위해서는 compare_strict위와 동일한 작업이 필요 하지만 이제 “보다 작음”과 “같음”이라는 두 가지 관심 대상이 있습니다. 이 cror두 비트를 하나로 결합 하려면 추가 명령어 ( -조건 레지스터 비트 OR)가 필요합니다 . 따라서 compare_loose5 개의 지침이 compare_strict필요하지만 4 개의 지침이 필요합니다.

컴파일러가 다음과 같이 두 번째 함수를 최적화 할 수 있다고 생각할 수 있습니다.

int compare_loose(double a, double b) { return ! (a > b); }

그러나 이것은 NaN을 잘못 처리합니다. NaN1 <= NaN2그리고 NaN1 > NaN2모두 필요가 false로 평가합니다.


답변

아마도 그 이름이없는 책의 저자는 읽은 a > 0것보다 더 빨리 읽히고 그것이 a >= 1보편적으로 사실이라고 생각했을 것입니다.

그러나는 0( CMP아키텍처에 따라, 예를 들어로 대체 될 수 있기 때문에)가 포함되어 OR있기 때문이 아니라 때문입니다 <.


답변

적어도 이것이 사실이라면 컴파일러는 <= b를! (a> b)로 사소하게 최적화 할 수 있으므로 비교 자체가 실제로 느리더라도 가장 순수한 컴파일러를 제외하고는 차이를 느끼지 못할 것입니다 .