가 if( a < 901 )
보다 더 빨리 if( a <= 900 )
.
이 간단한 예제와 정확히 같지는 않지만 루프 복잡한 코드에서 약간의 성능 변화가 있습니다. 나는 이것이 사실 일 경우에 생성 된 머신 코드로 무언가를해야한다고 생각한다.
<
것이 입력하는 것보다 두 배 빠릅니다 <=
.
가 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:
따라서 둘 사이의 유일한 차이점은 명령 jg
대 jge
명령입니다. 두 사람은 같은 시간이 걸립니다.
다른 점프 명령이 같은 시간이 걸린다는 것을 나타내는 것은 아무것도 없습니다. 이것은 대답하기가 약간 까다 롭지 만 여기에 내가 줄 수있는 것이 있습니다 .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
jg
와 jnle
같은 명령입니다 7F
:-)
역사적으로 (우리는 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 속도 비율과 관련이 있었지만 오늘날에는 거의 관련이 없습니다.
jge
하여 제로 및 부호 / 캐리 플래그를 모두 테스트합니다.
<=
시험에서 구현 될 수있다 하나 개의 피연산자가 스와핑을 위해 테스트를 지시 not <
(당량 >=
이것은 바람직하다) <=
교환 피연산자 : cmp B,A; bcs addr
.
내부 정수 유형에 대해 이야기하고 있다고 가정하면 하나가 다른 정수보다 빠를 수있는 방법은 없습니다. 그것들은 의미 상 분명히 동일합니다. 둘 다 컴파일러에게 정확히 동일한 작업을 수행하도록 요청합니다. 끔찍하게 고장난 컴파일러 만이 이들 중 하나에 대해 열등한 코드를 생성합니다.
일부 플랫폼 있었다면 <
보다 더 빨리이었다 <=
간단한 정수 유형의 컴파일러한다 항상 변환 <=
에 <
상수. 나쁜 컴파일러가 아닌 모든 컴파일러 (플랫폼 용).
<
없습니다 <=
. 당신은 ... 그들은 일반적으로 이미 죽은 코드 최적화, 꼬리 호출 최적화, (경우에 및 줄이기) 루프 리프팅, 다양한 루프의 자동 parallelisation 등을 실시 할 것을 고려할 때 컴파일러에 대한 매우 간단한 최적화입니다 왜 시간을 낭비 조기 최적화를 숙고 ? 프로토 타입을 실행하고 프로파일 링하여 가장 중요한 최적화가 어디에 있는지 파악하고, 중요도에 따라 최적화를 수행하고 진척도를 측정하는 방식에 따라 프로파일을 다시 수행하십시오 ...
(a < C)
에 대한 (a <= C-1)
(일부 상수가 C
) 발생 C
명령어 세트 인코딩하는데 더 어렵게한다. 예를 들어, 명령어 세트는 비교에서 압축 형식으로 -127에서 128 사이의 부호있는 상수를 나타낼 수 있지만 해당 범위 밖의 상수는 더 길거나 느린 인코딩 또는 다른 명령어를 사용하여로드해야합니다. 따라서 같은 비교 (a < -127)
에는 간단한 변환이 없을 수 있습니다.
a > 127
a > 128
a > 127
a >= 128
<=
에 <
상수가". 내가 아는 한, 그 변환에는 상수 변경이 포함됩니다. 예를 들어 빠르기 때문에 a <= 42
컴파일됩니다 . 일부 엣지의 경우 새로운 상수가 더 많거나 느린 명령을 요구할 수 있기 때문에 그러한 변환은 유익하지 않습니다. 물론 과 동일하고, 컴파일러는 (같은) 가장 빠른 방법은 모두 형태를 인코딩해야하지만, 그게 내가 한 말과 일치하지 않습니다. a < 43
<
a > 127
a >= 128
둘 다 빠르지 않다는 것을 알았습니다. 컴파일러는 각 조건에서 다른 값으로 동일한 기계 코드를 생성합니다.
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
if(a <=900)
정확히 같은 asm을 생성 한다는 것을 증명하기 위해 그것을 사용해야한다고 생각합니다. :)
부동 소수점 코드의 경우 <= 비교는 현대 아키텍처에서도 실제로 (한 명령으로) 느려질 수 있습니다. 첫 번째 기능은 다음과 같습니다.
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_loose
5 개의 지침이 compare_strict
필요하지만 4 개의 지침이 필요합니다.
컴파일러가 다음과 같이 두 번째 함수를 최적화 할 수 있다고 생각할 수 있습니다.
int compare_loose(double a, double b) { return ! (a > b); }
그러나 이것은 NaN을 잘못 처리합니다. NaN1 <= NaN2
그리고 NaN1 > NaN2
모두 필요가 false로 평가합니다.
fucomip
ZF와 CF를 설정합니다.
cr
는 x86 ZF
과 같은 플래그와 같습니다 CF
. (CR이 더 융통성이 있지만) 포스터가 말한 것은 결과를 GPR로 옮기는 것입니다. PowerPC에 대해 두 가지 명령을 수행하지만 x86에는 조건부 이동 명령이 있습니다.
아마도 그 이름이없는 책의 저자는 읽은 a > 0
것보다 더 빨리 읽히고 그것이 a >= 1
보편적으로 사실이라고 생각했을 것입니다.
그러나는 0
( CMP
아키텍처에 따라, 예를 들어로 대체 될 수 있기 때문에)가 포함되어 OR
있기 때문이 아니라 때문입니다 <
.
(a >= 1)
보다 느린 속도로 실행 하는 데 나쁜 컴파일러가 필요 하다 (a > 0)
.
적어도 이것이 사실이라면 컴파일러는 <= b를! (a> b)로 사소하게 최적화 할 수 있으므로 비교 자체가 실제로 느리더라도 가장 순수한 컴파일러를 제외하고는 차이를 느끼지 못할 것입니다 .
NOT
단지 다른 명령 (의해 이루어진다 je
대 jne
)
이것은 C가 컴파일되는 기본 아키텍처에 크게 의존합니다. 일부 프로세서와 아키텍처에는 서로 다른 횟수의 사이클에서 실행되는 것과 같거나 작거나 같은 명시 적 명령이있을 수 있습니다.
그러나 컴파일러가 문제를 해결할 수 있기 때문에 매우 드문 일입니다.
대부분의 아키텍처, 컴파일러 및 언어 조합은 더 빠르지 않습니다.
다른 답변에 집중 한 86 아키텍처, 그리고 내가 모르는 ARM의 생성 된 코드에 특별히 언급을 충분히합니다 (예를 들어, 어셈블러 것 같다) 아키텍처를, 그러나 이것은의 예입니다 마이크로 최적화 입니다 매우 아키텍처 구체적이고 최적화가되는 것처럼 안티-최적화 일 가능성이 높습니다 .
따라서 이런 종류의 마이크로 최적화 는 최고의 소프트웨어 엔지니어링 방식이 아닌 화물 컬트 프로그래밍 의 예 라고 제안합니다 .
이 아마 몇몇 이 최적화입니다 아키텍처는,하지만 난 그 반대가 사실 일 수도 적어도 하나의 아키텍처 알고있다. 유서 깊은 Transputer의 아키텍처에만 기계 코드 명령했다 동등 및 보다 크거나 같음 모든 비교는 이러한 프리미티브에서 구축 할 수 있었다, 그래서.
그럼에도 불구하고, 거의 모든 경우에, 컴파일러는 실제로 어떤 비교도 다른 어떤 것보다 이점이없는 방식으로 평가 명령을 주문할 수 있습니다. 최악의 경우, 피연산자 스택 에서 상위 2 개의 항목을 바꾸려면 리버스 명령어 (REV)를 추가해야 할 수도 있습니다. . 단일 바이트 명령으로 단일 사이클이 실행되었으므로 가능한 가장 작은 오버 헤드가있었습니다.
이와 같은 마이크로 최적화가 최적화 인지 안티 최적화 인지 는 사용중인 특정 아키텍처에 따라 다르므로 일반적으로 아키텍처 별 마이크로 최적화를 사용하는 습관을들이는 것은 나쁜 생각입니다. 그렇지 않으면 본능적으로 부적절 할 때 하나를 사용하십시오. 읽은 책이 옹호하는 것과 정확히 같습니다.
차이가 있더라도 차이점을 알 수 없습니다. 게다가 실제로 마법 상수를 사용하지 않는 한 추가 작업을 수행 a + 1
하거나 a - 1
조건을 유지해야합니다. 매우 나쁜 습관입니다.
여분의 문자로 인해 코드 처리가 약간 느려지기 때문에 대부분의 스크립팅 언어에서 행이 정확하다고 말할 수 있습니다. 그러나 최고 답변이 지적했듯이 C ++에는 영향을 미치지 않아야하며 스크립팅 언어로 수행되는 작업은 아마도 최적화와 관련이 없을 것입니다.
이 답변을 쓸 때 상수 a < 901
vs 의 구체적인 예가 아니라 <vs. <=에 대한 제목 질문 만 보았습니다 a <= 900
. 대부분의 컴파일러는 항상 사이의 변환 상수의 크기를 축소 <
하고<=
예를 들어, 때문에 86 즉시 피연산자 -128..127에 대한 짧은 1 바이트 인코딩을해야합니다.
ARM 및 특히 AArch64의 경우 즉시 인코딩 할 수있는 것은 좁은 필드를 단어의 임의의 위치로 회전 할 수 있는지에 달려 있습니다. 따라서 cmp w0, #0x00f000
인코딩 할 수는 있지만 그렇지 cmp w0, #0x00effff
않을 수도 있습니다. 따라서 비교를위한 더 작은 규칙과 컴파일 타임 상수가 항상 AArch64에 적용되는 것은 아닙니다.
대부분의 컴퓨터에서 어셈블리 언어로 비교할 때 비교하는 <=
비용은<
. 이것은 당신이 그것에 분기하거나, 0/1 정수를 만들기 위해 그것을 부울하거나, xless CMOV와 같은 분기없는 선택 연산의 술어로 사용하든 적용됩니다. 다른 답변은 질문 의이 부분만을 다루었습니다.
그러나이 질문은 C ++ 연산자, 최적화 프로그램 의 입력 에 관한 것입니다. 일반적으로 둘 다 동일하게 효율적입니다. 컴파일러가 항상 asm으로 구현 한 비교를 변환 할 수 있기 때문에이 책의 조언은 완전히 허구 적으로 들립니다. 그러나 적어도 하나의 예외가 있습니다.<=
실수로 컴파일러가 최적화 할 수없는 것을 만들 수 있습니다.
루프 조건으로서 컴파일러가 루프가 무한하지 않다는 것을 증명하지 못하게하는 경우와 질적으로 다른 경우 <=
가 있습니다. <
이는 자동 벡터화를 비활성화하여 큰 차이를 만들 수 있습니다.
부호없는 오버플로는 부호있는 오버플로 (UB)와 달리 base-2 랩으로 잘 정의되어 있습니다. 부호있는 루프 카운터는 일반적으로 부호없는 오버플로 UB를 기반으로 최적화하는 컴파일러를 통해 이로부터 안전 ++i <= size
합니다. 결국에는 항상 거짓이됩니다. ( 모든 C 프로그래머가 정의되지 않은 행동에 대해 알아야 할 것 )
void foo(unsigned size) {
unsigned upper_bound = size - 1; // or any calculation that could produce UINT_MAX
for(unsigned i=0 ; i <= upper_bound ; i++)
...
컴파일러는 정의 되지 않은 동작을 유발하는 값 을 제외하고 가능한 모든 입력 값에 대해 C ++ 소스의 (정의되고 법적으로 관찰 가능한) 동작을 유지하는 방식으로 만 최적화 할 수 있습니다 .
(간단한 i <= size
문제도 발생할 수 있지만 상한을 계산하는 것은 실수로 신경 쓰지 않아도되지만 컴파일러가 고려해야하는 입력에 대해 무한 루프의 가능성을 실수로 도입하는보다 현실적인 예라고 생각했습니다.)
이 경우 size=0
에 리드 upper_bound=UINT_MAX
, 그리고 i <= UINT_MAX
항상 사실이다. 따라서이 루프는 무한대이며 size=0
컴파일러는 프로그래머가 크기 = 0을 전달하려고하지 않더라도 존중해야 합니다. 컴파일러가 size = 0이 불가능하다는 것을 증명할 수있는 호출자에게이 함수를 인라인 할 수 있다면 훌륭합니다 i < size
.
asm if(!size) skip the loop;
do{...}while(--size);
은 for( i<size )
루프 내에서 실제 값이 i
필요하지 않은 경우 루프 를 최적화하는 일반적으로 효율적인 방법 중 하나입니다 ( 루프가 항상 "do ... while"스타일 (테일 점프)로 컴파일되는 이유는 무엇입니까? ).
그러나 이것은 무한대 일 수 없지만 {}로 입력 size==0
하면 2 ^ n의 반복이 발생합니다. ( for 루프 C 에서 부호없는 정수를 반복 하면 0을 포함한 모든 부호없는 정수에 대해 루프를 표현할 수 있지만 캐리 플래그가 없으면 asm 방식으로는 쉽지 않습니다.)
루프 카운터의 랩 어라운드 (wraparound)가 가능성이 있기 때문에, 현대 컴파일러는 종종 "포기"하고 거의 적극적으로 최적화하지 않습니다.
unsigned i <= n
를 사용하면sum(1 .. n)
가우스 n * (n+1) / 2
공식을 기반으로 닫힌 형태로 루프 를 최적화하는 clang의 관용구 인식이 무효화 됩니다.
unsigned sum_1_to_n_finite(unsigned n) {
unsigned total = 0;
for (unsigned i = 0 ; i < n+1 ; ++i)
total += i;
return total;
}
Godbolt 컴파일러 탐색기에서 clang7.0 및 gcc8.2의 x86-64 asm
# clang7.0 -O3 closed-form
cmp edi, -1 # n passed in EDI: x86-64 System V calling convention
je .LBB1_1 # if (n == UINT_MAX) return 0; // C++ loop runs 0 times
# else fall through into the closed-form calc
mov ecx, edi # zero-extend n into RCX
lea eax, [rdi - 1] # n-1
imul rax, rcx # n * (n-1) # 64-bit
shr rax # n * (n-1) / 2
add eax, edi # n + (stuff / 2) = n * (n+1) / 2 # truncated to 32-bit
ret # computed without possible overflow of the product before right shifting
.LBB1_1:
xor eax, eax
ret
그러나 순진한 버전의 경우 clang에서 바보 루프를 얻습니다.
unsigned sum_1_to_n_naive(unsigned n) {
unsigned total = 0;
for (unsigned i = 0 ; i<=n ; ++i)
total += i;
return total;
}
# clang7.0 -O3
sum_1_to_n(unsigned int):
xor ecx, ecx # i = 0
xor eax, eax # retval = 0
.LBB0_1: # do {
add eax, ecx # retval += i
add ecx, 1 # ++1
cmp ecx, edi
jbe .LBB0_1 # } while( i<n );
ret
GCC는 닫힌 형식을 사용하지 않으므로 루프 조건을 선택해도 실제로 손상되지 않습니다 . i
XMM 레지스터의 요소에서 4 개의 값을 병렬로 실행하여 SIMD 정수 추가로 자동 벡터화됩니다 .
# "naive" inner loop
.L3:
add eax, 1 # do {
paddd xmm0, xmm1 # vect_total_4.6, vect_vec_iv_.5
paddd xmm1, xmm2 # vect_vec_iv_.5, tmp114
cmp edx, eax # bnd.1, ivtmp.14 # bound and induction-variable tmp, I think.
ja .L3 #, # }while( n > i )
"finite" inner loop
# before the loop:
# xmm0 = 0 = totals
# xmm1 = {0,1,2,3} = i
# xmm2 = set1_epi32(4)
.L13: # do {
add eax, 1 # i++
paddd xmm0, xmm1 # total[0..3] += i[0..3]
paddd xmm1, xmm2 # i[0..3] += 4
cmp eax, edx
jne .L13 # }while( i != upper_limit );
then horizontal sum xmm0
and peeled cleanup for the last n%3 iterations, or something.
또한 평범한 스칼라 루프가있어 매우 작 n
거나 무한 루프 경우에 사용한다고 생각합니다 .
BTW에서이 두 루프는 루프 오버 헤드에 대한 명령 (및 Sandybridge 제품군 CPU의 UOP)을 낭비합니다. sub eax,1
/ cmp / jcc jnz
대신 add eax,1
/가 더 효율적입니다. 2 대신 1uop (sub / jcc 또는 cmp / jcc의 매크로 융합 후). 두 루프 후의 코드는 EAX를 무조건 작성하므로 루프 카운터의 최종 값을 사용하지 않습니다.
<
또는 C에서는 그렇지 않습니다 <=
. 그러나 ZF가 설정되어 있거나 (ecx == 0) CF가 설정되어 있으면 (EAX == 1의 비트 3) , test ecx,ecx
/ bt eax, 3
/ jbe
는 점프합니다. 읽은 플래그가 전부가 아니기 때문에 대부분의 CPU에서 부분 플래그가 정지됩니다 마지막 명령에서 플래그를 작성하십시오. Sandybridge 제품군에서는 실제로 멈추지 않으며 병합 Uop 만 삽입하면됩니다. cmp
/ test
모든 플래그를 쓰지만 bt
ZF는 수정하지 않습니다. felixcloutier.com/x86/bt
컴퓨터를 만든 사람들이 부울 논리에 나쁜 경우에만. 그들은해서는 안됩니다.
모든 비교 ( >=
<=
>
<
)는 동일한 속도로 수행 할 수 있습니다.
모든 비교는, 단지 빼기 (차이)이고 그것이 긍정적 / 부정적인지 보는 것입니다.
(를 msb
설정하면 숫자는 음수입니다)
확인하는 방법 a >= b
? 하위 긍정적 a-b >= 0
인지 확인하십시오 a-b
.
확인하는 방법 a <= b
? 하위 긍정적 0 <= b-a
인지 확인하십시오 b-a
.
확인하는 방법 a < b
? 하위 음수 a-b < 0
인지 확인하십시오 a-b
.
확인하는 방법 a > b
? 하위 음수 0 > b-a
인지 확인하십시오 b-a
.
간단히 말해, 컴퓨터는 주어진 op의 후드 아래 에서이 작업을 수행 할 수 있습니다.
a >= b
== msb(a-b)==0
a <= b
== msb(b-a)==0
a > b
== msb(b-a)==1
a < b
==msb(a-b)==1
물론 컴퓨터는 실제로 ==0
또는 그 ==1
중 하나 를 수행 할 필요가 없습니다 .
을 위해 ==0
그것은 단지를 반전 할 수 msb
회로에서.
어쨌든, 그들은 확실히 롤로 a >= b
계산 되지 않았을 것입니다a>b || a==b