관계 연산자는 두 값을 비교하는 데 사용된다. 관계 연산자에는 아래와 같은 연산자가 있다.
==
(같다)!=
(같지 않다)>
(크다)<
(작다)>=
(크거나 같다)<=
(작거나 같다)관계 연산자가 포함된 코드를 작성하면, 컴퓨터는 먼저 문법을 분석하고 코드를 토큰화한다. 소스 코드는 코드의 요소를 나타내는 일련의 토큰으로 변환된다.
예를 들어, a > b
라는 표현식은 세 부분으로 토큰화된다.
a
(변수)>
(관계 연산자)b
(변수)토큰화가 완료된 후, 컴파일러나 인터프리터는 의미 분석을 수행하여 연산이 유효한지 확인한다. 여기에는 a
와 b
의 타입이 >
연산자와 호환되는지 확인하는 작업이 포함된다. 예를 들어, a
와 b
는 일반적으로 숫자 타입이거나 비교 가능한 타입이어야 한다.
그 다음 단계는 표현식을 중간 표현(IR*)으로 변환하는 것이다. 이는 실제 하드웨어보다 추상화된 낮은 수준의 코드이다. 예를 들어, a > b
는 CMP a, b
와 같은 중간 언어 명령어로 번역될 수 있다.
*IR: Intermediate Representation. 컴파일러가 소스 코드를 기계어로 번역하는 과정에서 사용하는 중간 단계의 표현 형식. IR은 컴파일러가 다양한 최적화와 코드 생성 작업을 수행할 수 있게 하는 중요한 역할을 한다.
중간 표현은 기계어로 번역된다. 기계어는 CPU의 아키텍처에 따라 다르지만 비교를 처리하는 특정 명령어가 포함된다.
예를 들어, x86 어셈블리 언어에서:
CMP
명령어는 두 값을 비교하는 데 사용된다.CMP
명령어 다음에는 JG
(크면 점프), JL
(작으면 점프), JE
(같으면 점프) 등과 같은 조건부 점프 명령어가 비교 결과에 따라 사용된다.예시:
CMP eax, ebx ; eax와 ebx 레지스터의 값을 비교
JG label ; eax가 ebx보다 크면 label로 점프
실행 중에 CPU는 다음 단계를 수행한다:
a
와 b
의 값을 레지스터나 메모리에서 가져온다.a == b
)a < b
)Let’s learn about
CMP
instruction
CMP
명령어는 두 피연산자를 비교하는 데 사용된다. 이 명령어는 피연산자를 뺄셈하여 플래그 레지스터의 값을 설정하지만, 실제 피연산자의 값은 변경하지 않는다. 이를 통해 조건부 분기 명령어와 함께 사용되어 프로그램의 흐름을 제어할 수 있다.CMP 명령어의 사용법
CMP
명령어의 일반적인 형식은 다음과 같다:CMP destination, source
여기서
destination
과source
는 비교할 두 피연산자다.destination
에서source
를 빼고 그 결과를 버린다. 대신, 상태 플래그 레지스터의 플래그가 설정된다.플래그 레지스터
CMP
명령어는 상태 플래그 레지스터의 여러 플래그를 설정한다. 주요 플래그는 다음과 같다:
- ZF (Zero Flag):
destination - source
의 결과가 0일 경우- SF (Sign Flag):
destination - source
의 결과가 음수일 경우- CF (Carry Flag):
destination
이source
보다 작을 경우(부호 없는 비교)- OF (Overflow Flag): 부호 있는 연산에서 오버플로가 발생할 경우
조건부 연산(예: if (a > b) { ... }
)의 경우 CPU는 비교에 의해 설정된 플래그를 사용하여 코드의 다른 부분으로 분기할지 여부를 결정한다.
예를 들어:
CMP eax, ebx ; eax와 ebx를 비교
JG greater_label ; 크면 greater_label로 점프
비분기 컨텍스트에서(예: result = a > b
와 같은 표현식), 비교 결과는 일반적으로 불리언 값(true
또는 false
)으로 레지스터나 메모리 위치에 저장된다.
다음 C++ 코드를 고려해 보자:
int a = 5, b = 3;
bool result = a > b;
a > b
표현식 분석 t1 = 5
t2 = 3
t3 = t1 > t2
MOV eax, 5 ; eax에 5를 로드
MOV ebx, 3 ; ebx에 3을 로드
CMP eax, ebx ; eax와 ebx를 비교
SETG al ; 크면 al 설정
이 단계를 통해 컴퓨터가 관계 연산자를 처리하는 복잡성과 효율성을 이해할 수 있으며, 하드웨어 수준에서 정확하고 효율적인 비교를 보장한다.
<
, <=
, >
, >=
중 어떤 연산자가 가장 효율적일까?모든 연산자가 플래그 레지스터를 검사하는 방식이 다르지만, 실질적인 연산 횟수나 복잡도 측면에서 큰 차이는 없다. 요즘 CPU의 성능 최적화 기술 덕분에 이러한 미세한 차이는 성능에 거의 영향을 미치지 않는다.
결론적으로 성능 차이는 매우 미미하므로 코드의 논리적 흐름과 가독성을 유지하는 것이 더 중요하다. 필요한 연산자를 사용하여 코드의 의도를 명확히 표현하는 것이 좋다.
<
연산자 (JL
- Jump if Less)SF != OF
<=
연산자 (JLE
- Jump if Less or Equal)ZF == 1 or SF != OF
>
연산자 (JG
- Jump if Greater)ZF == 0 and SF == OF
>=
연산자 (JGE
- Jump if Greater or Equal)SF == OF
*참고: http://unixwiz.net/techtips/x86-jumps.html
어떤 연산자를 써도 성능 차이가 미미하지만 플래그 검사 측면에서 가장 적은 비교를 수행하는 연산자는 <
연산자 (JL
)와 >=
연산자 (JGE
)다.