현대 CPU의 마법: OoO 실행 및 슈퍼스칼라 아키텍처
-
Zen 2+와 Zen 3 CPU의 성능 차이를 예시로 들며, CPU 주파수 증가율보다 훨씬 높은 단일 스레드 성능 향상(2% vs 25%)의 원인을 설명합니다.
-
1990년대 펜티엄급 CPU부터 x86은 RISC CPU를 따라 슈퍼스칼라 시대로 진입했습니다. 이는 한 사이클에 여러 명령어를 동시에 실행할 수 있게 합니다.
-
f(a, b, c, d)예시 코드를 통해 독립적인 연산이 동시에 처리될 수 있음을 보여줍니다. -
OoO(Out-of-Order) 실행은 명령어 의존성을 분석하여 CPU가 유휴 시간을 줄이고 더 빠르게 작업을 완료하도록 돕는 핵심 기술입니다.
-
분기 예측(branch prediction)은
if/else와 같은 조건문에서 CPU가 미리 결과를 예측하여 실행하고, 예측이 틀리면 되돌리는 방식으로 성능을 극대화합니다. 이는 멜트다운(Meltdown) 같은 보안 문제의 원인이 되기도 했지만, 성능 이점 때문에 비활성화할 수 없습니다.
인터프리터에 미치는 영향
-
대부분의 인터프리터는 중간 표현(opcode)을 사용하며, 일반적인
switch문 기반의 메인 루프는 분기 예측 실패로 인해 성능 저하를 겪습니다. -
파이썬(Python)과 PostgreSQL 같은 프로젝트에서 사용되는 “computed gotos” 기술은
switch문을 제거하여 인터프리터 속도를 15-20% 향상시킬 수 있습니다. 이는 연속적인 명령어 실행에서 분기 예측을 더 쉽게 만들어 CPU 효율을 높입니다.
PostgreSQL 인터프리터 최적화: SELECT a FROM table WHERE a = 42 예시
-
간단한
int4eq비교 함수를 예로 들어 PostgreSQL의EEOP_SCAN_FETCHSOME,EEOP_SCAN_VAR,EEOP_FUNCEXPR_STRICT_2,EEOP_QUAL,EEOP_DONE_RETURN오프코드를 분석합니다. -
NULL 검사 최적화:
FUNCEXPR_STRICT_2오프코드에서 상수 인수에 대한 불필요한 NULL 검사를 제거함으로써 약 9.5%의 성능 향상(124ms -> 115ms)을 확인했습니다. CPU는 패턴을 학습하지만, 그래도 비용이 발생합니다. -
함수 인라인 최적화:
int4eq함수 호출을 오프코드 내부에 직접 인라인하여FUNCEXPR_STRICT_INT4EQ와 같은 전용 오프코드를 생성하면, 명령어 수와 메모리 주소 점프를 줄여 약 10.3%의 성능 향상(127ms -> 114ms)을 달성할 수 있습니다. -
이러한 최적화는 JIT 컴파일러뿐만 아니라 인터프리터에도 동일하게 적용될 수 있으며, JIT 컴파일러가 인터프리터를 능가하기 어렵게 만듭니다.
JIT 컴파일러의 도전
-
초기에는 NULL 검사 제거를 JIT에서만 구현할 수 있다고 생각했지만, 인터프리터에서도 쉽게 구현 가능함을 깨달았습니다.
-
int4eq와 같은 일반적인 연산을 위한 특수 오프코드 추가 역시 인터프리터에서 가능하여 JIT의 성능 이점을 최소화합니다. -
현대 CPU의 분기 예측 및 기타 하드웨어 최적화는 인터프리터의 성능 병목을 크게 줄여, JIT 컴파일러가 압도적인 우위를 점하기 어렵게 만듭니다.