c++ - 활용 - 프로그래밍 최적화 기법




<보다 빠르고<=입니까? (9)

저자 if( a < 901 )if( a <= 900 ) 보다 빠르다고 말하는 저자의 책을 읽었습니다.

이 간단한 예제와 똑같은 것은 아니지만 루프 복잡한 코드에서 약간의 성능 변화가 있습니다. 이것이 사실이라면 생성 된 기계 코드로 무언가를해야한다고 생각합니다.


TL;DR 응답

대부분의 아키텍처, 컴파일러 및 언어 조합의 경우 더 빠르지 않습니다.

정답

다른 대답은 x86 아키텍처에 집중되어 있으며, 생성 된 코드에 대해 특별히 언급할만한 ARM 아키텍처 (예제 어셈블러 인 것 같습니다)는 모르지만, 이것은 매우 micro-optimisationmicro-optimisation 의 예입니다 특정 적이며, 최적화가 아닌 반 최적화될 가능성이 큽니다 .

따라서, 나는 이런 종류의 micro-optimisation 가 최상의 소프트웨어 공학 실습 이라기보다는 화물 컬트 프로그래밍의 예라고 제안 할 것이다.

아마도 이것이 최적화 인 아키텍처가 있을지 모르지만, 그 반대가 사실 일 수있는 적어도 하나의 아키텍처에 대해 알고 있습니다. 훌륭한 Transputer 아키텍처는 거나 더 크거나 같은 머신 코드 명령어만을 가지고 있었기 때문에 이러한 모든 프리미티브를 모든 비교 작업에 적용해야했습니다.

심지어 거의 모든 경우에, 컴파일러는 실제적으로 비교가 다른 어떤 것보다 이점을 갖지 않는 방식으로 평가 지시를 주문할 수 있습니다. 최악의 경우지만, 피연산자 스택 의 상위 두 항목을 바꾸려면 역방향 명령 (REV)을 추가해야 할 수도 있습니다. 이것은 단일 사이클을 실행하는 단일 바이트 명령 이었으므로 가능한 가장 작은 오버 헤드가있었습니다.

이와 같은 미세 최적화가 최적화 또는 최적화 가 아닌지 여부는 사용중인 특정 아키텍처에 따라 다르므로 아키텍처 별 미세 최적화를 사용하는 습관을 갖지 않는 것이 좋습니다. 그렇지 않으면 본능적으로 그것을하는 것이 부적당 할 때 그것을 사용 하거든, 당신이 읽고있는 책이 옹호하고있는 무슨 정확하게 인처럼 본다.


그들은 같은 속도를 가지고 있습니다. 어쩌면 일부 특수 아키텍처에서 그 / 그녀가 말한 바는 맞지만 x86 패밀리에서는 적어도 동일하다고 알고 있습니다. 이렇게하기 위해 CPU는 빼기 (a - b)를 수행 한 다음 플래그 레지스터의 플래그를 확인합니다. 이 레지스터의 두 비트는 ZF (제로 플래그) 및 SF (부호 플래그)라고하며, 하나의 마스크 연산으로 수행하기 때문에 한 사이클로 완료됩니다.


실제로 어셈블리 레벨에서 둘 다 한 줄을 사용하기 때문에 정확히 같은 속도가됩니다. 예를 들면 :

  • jl ax,dx (AX가 DX보다 작 으면 점프)
  • jle ax,dx (AX가 DX보다 작거나 같은 경우 점프)

따라서 더 빠르지도 않습니다. 그러나 만약 여러분이 정말로 기술적 인 사고를 원한다면 전자 전류 레벨에서 그것을 검사한다면 속도가 약간 빨라지 겠지만 속도에 가깝지는 않을 것입니다.


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

  • EFLAGS 를 설정하는 test 또는 cmp 명령
  • 비교 유형 (및 코드 레이아웃)에 따라 Jcc (점프) 명령어가 제공됩니다 .
    • jne - 같지 않으면 점프 -> ZF = 0
    • jz - 0 인 경우 점프 -> ZF = 1
    • jg - 큰 경우 점프 -> ZF = 0 and SF = OF
    • (기타...)

예제 (간략하게 편집 됨) $ gcc -m32 -S -masm=intel test.c 컴파일 $ 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:

따라서이 둘의 유일한 차이점은 jg 명령과 jge 명령입니다. 두 사람은 같은 시간이 걸릴 것입니다.

다른 점프 명령어가 같은 양의 시간을 차지한다는 것을 아무 것도 가리 키지 않는다는 설명에 답하고 싶습니다. 이 답변은 약간 까다 롭습니다.하지만 여기에 나와 있습니다. Intel 명령어 세트 참조 에서는 공통 명령 인 Jcc (조건이 충족되면 Jcc 뛰기)로 모두 그룹화됩니다. 부록 C, 지연 시간 및 처리량의 최적화 참조 설명서 에서 동일한 그룹화가 함께 이루어집니다.

대기 시간 (Latency) - 실행 코어가 명령을 구성하는 모든 μops의 실행을 완료하는 데 필요한 클럭 사이클 수입니다.

처리량 - 이슈 포트가 동일한 명령어를 다시 허용하기 전에 대기해야하는 클럭 사이클 수입니다. 많은 명령어의 경우 명령어의 처리량이 대기 시간보다 훨씬 짧을 수 있습니다

Jcc 값은 Jcc 과 같습니다.

      Latency   Throughput
Jcc     N/A        0.5

Jcc 에는 다음과 같은 주석이 Jcc .

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

따라서 Intel docs의 어떤 것도 Jcc 명령어를 다른 명령어와 다르게 취급하지 않습니다.

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

편집 : 부동 소수점

이것은 x87 부동 소수점에도 적용됩니다. (위와 거의 같은 코드이지만 int 대신 double 사용합니다.)

        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

어쩌면 그 이름없는 책의 저자는 a > 0a >= 1 보다 빨리 달리고 그것이 보편적으로 진실이라고 생각합니다.

하지만 0 이 관련되어 있기 때문입니다 ( CMP 가 아키텍처에 따라 예를 들어 OR 로 대체 될 수 있기 때문에).


여분의 문자로 인해 코드 처리가 약간 느려지므로 대부분의 스크립팅 언어에서 그 행이 맞다고 말할 수 있습니다. 그러나 최상위 답변이 지적한대로 C ++에는 효과가 없어야하며 스크립팅 언어로 수행되는 작업은 최적화에 관심이없는 것 같습니다.


우리가 내부 정수형에 대해서 이야기하고 있다고 가정하면, 다른 것보다 더 빠를 수있는 방법이 없다. 그들은 분명히 의미 상 동일합니다. 둘 다 컴파일러에게 똑같은 일을하도록 요청합니다. 끔찍하게 깨진 컴파일러 만이 이들 중 하나에 대해 열등한 코드를 생성합니다.

간단한 정수형에 대해 <= 보다 빠른 <= 가있는 플랫폼이있는 경우 컴파일러는 상수에 대해 항상 <= < 로 변환해야합니다. 모든 컴파일러는 그 플랫폼에 맞는 나쁜 컴파일러가 아닙니다.


이는 C가 컴파일되는 기본 아키텍처에 크게 의존합니다. 일부 프로세서 및 아키텍처에는 서로 다른 수의 사이클에서 실행되는 명시 적 명령이 있거나 작거나 같을 수 있습니다.

컴파일러가 문제를 해결하여 관련성이 없으므로 상당히 이상한 일입니다.


최소한 이것이 사실이라면 컴파일러는 a = b를! a (b>)로 trivially 최적화 할 수 있습니다. 따라서 비교 자체가 실제로 느린 경우 라 할지라도 가장 순진한 컴파일러를 제외하고는 차이점을 느끼지 못할 것입니다 .





relational-operators