처리중입니다. 잠시만 기다려주세요.
TTJ 코딩클래스
정규반 단과 자료실 테크 뉴스 코딩 퀴즈
테크 뉴스
Hacker News 2026.04.23 25
#AI

왜 레지스터를 0으로 만들 때 SUB이 아니라 XOR을 쓸까? - 어셈블리 고전 퀴즈의 진짜 답

Hacker News 원문 보기
왜 레지스터를 0으로 만들 때 SUB이 아니라 XOR을 쓸까? - 어셈블리 고전 퀴즈의 진짜 답

질문부터 던져볼게요

어셈블리 코드를 들여다보다 보면 희한한 장면을 자주 만나요. 어떤 레지스터를 0으로 초기화할 때 개발자들이 죄다 XOR eax, eax 이렇게 쓰거든요. 직역하면 "eax와 eax를 XOR 연산해라"인데, 같은 값끼리 XOR하면 무조건 0이 나오니까 결국 "eax를 0으로 만들어라"가 되는 거죠. 근데 여기서 궁금증이 생겨요. 같은 값끼리 빼도 0이 되잖아요? SUB eax, eax 이렇게 쓰면 되지 않나? 아니면 아예 MOV eax, 0 이렇게 직관적으로 쓰면 될 텐데, 왜 굳이 XOR일까요?

마이크로소프트의 유명 개발자 블로거 Raymond Chen이 이 오래된 질문을 다시 풀어줬어요. 정답은 단순해 보이지만 CPU 내부 동작까지 내려가야 제대로 이해가 됩니다.

일단 기계어 크기부터 비교해봅시다

MOV eax, 0은 x86에서 5바이트를 차지해요. opcode 1바이트에 32비트 상수값 0이 4바이트 붙거든요. 반면 XOR eax, eax2바이트면 끝납니다. SUB eax, eax도 똑같이 2바이트예요. 코드 크기가 작다는 건 CPU의 명령어 캐시(L1 instruction cache)에 더 많은 코드가 들어갈 수 있다는 뜻이고, 이건 성능에 꽤 큰 영향을 줘요. 그래서 "0 만들 때 MOV은 비효율적이다"는 건 오래전부터 상식이었어요.

그럼 XOR이랑 SUB 둘 다 2바이트인데, 왜 XOR이 승자일까요?

진짜 이유는 CPU 안쪽에 있어요

여기서 의존성 체인(dependency chain)이라는 개념이 나와요. 이게 뭐냐면, 현대 CPU는 명령어를 순서대로 하나씩 처리하지 않아요. 여러 명령어를 동시에 파이프라인에 밀어 넣고 병렬로 실행하거든요. 이걸 비순차 실행(out-of-order execution)이라고 해요. 근데 파이프라인에 밀어 넣을 때 한 가지 조건이 있어요. '이 명령어가 쓰는 입력값이 준비되어 있어야 한다'는 거죠.

예를 들어 SUB eax, eax를 CPU가 봤다고 쳐봅시다. 이 명령어는 '현재 eax 값에서 현재 eax 값을 뺀다'는 뜻이에요. CPU는 "아, 나는 지금 eax 값이 필요하네?" 하고 생각해요. 그래서 eax 값이 확정될 때까지 기다립니다. 앞에서 eax를 계산하던 긴 연산이 있었다면 그게 다 끝날 때까지 이 명령어는 대기 상태에 놓이는 거예요. 사람 눈에는 "같은 값끼리 빼니까 당연히 0이잖아" 싶지만, CPU 입장에서는 그런 수학적 추론을 안 합니다. 형식적으로 eax를 입력으로 받기 때문에 의존성이 있는 거죠.

반면 XOR reg, reg는 인텔과 AMD가 수십 년 전부터 '영점화 관용구(zeroing idiom)'로 특별 취급하고 있어요. CPU 디코더가 "아, 같은 레지스터끼리 XOR이네? 이건 입력값 필요 없이 그냥 0으로 만들라는 뜻이구나" 하고 알아챕니다. 그러면 이전 연산이 eax를 계산하고 있든 말든 의존성을 완전히 끊어버리고 바로 0을 넣어버려요. 결과적으로 파이프라인에서 대기할 일이 없어지는 거죠.

잠깐, 그런데 요즘 CPU는 SUB도 똑같이 처리하지 않나요?

좋은 지적이에요. 사실 비교적 최근 CPU들(인텔 Sandy Bridge 이후, AMD도 비슷한 시기부터)은 SUB reg, reg도 영점화 관용구로 인식해서 의존성을 끊어주기 시작했어요. 그래서 성능상 차이는 이제 거의 없어졌어요. 그런데도 왜 다들 여전히 XOR을 쓰느냐? 하위 호환성과 관행 때문이에요.

XOR은 x86 초창기부터 모든 CPU에서 영점화로 최적화됐어요. SUB은 비교적 최근에야 최적화됐고요. 그래서 오래된 CPU에서도 안전하게 빠른 코드를 내려면 XOR이 무조건 우위에 있었어요. 컴파일러 개발자들, 어셈블리 마스터들이 수십 년 동안 XOR을 써왔기 때문에 이게 사실상의 표준 관용구가 된 거죠. 코드 리뷰할 때 SUB eax, eax를 보면 "어? 왜 굳이?" 싶어지는, 그런 문화적 약속인 거예요.

플래그 동작도 미묘하게 달라요

하나 더 있어요. XOR과 SUB은 플래그 레지스터(CPU의 상태 플래그)에 동일한 결과를 남기긴 해요. 둘 다 ZF(Zero Flag)를 1로 세팅하죠. 근데 코드 의도를 읽는 사람 입장에서 보면 XOR eax, eax는 "이건 영점화구나"라고 바로 읽히고, SUB eax, eax는 "빼기 연산을 왜 자기 자신이랑 하지?" 하고 한 번 더 생각하게 만들어요. 가독성 측면에서도 XOR이 관용구로 확고하게 자리잡은 이유가 여기 있어요.

한국 개발자에게 주는 시사점

어셈블리를 직접 쓸 일이 많지 않다면 이런 지식이 당장 쓸모없어 보일 수 있어요. 그런데 저수준 성능 튜닝을 할 일이 생기면 반드시 필요한 감각이에요. 예를 들어 게임 엔진, 영상 코덱, 고빈도 거래 시스템, 데이터베이스 내부 같은 곳에서는 여전히 SIMD 명령어나 인라인 어셈블리를 만지고, 이때 의존성 체인을 이해하지 못하면 최적화를 해도 왜 빨라지지 않는지 감을 못 잡게 됩니다.

더 중요한 건 "내 코드를 CPU가 어떻게 보는가"라는 관점이에요. 우리가 쓰는 고수준 언어에서도 결국 이런 원리가 깔려 있어요. 예를 들어 루프를 돌 때 불필요한 의존성을 만들면 벡터화가 안 되고, 멀티스레드 코드에서 false sharing이 생기면 캐시 라인 때문에 성능이 죽는 것도 비슷한 레벨의 이야기죠. 컴파일러가 왜 이렇게 코드를 뽑는지 납득이 되려면 이런 기본기가 있어야 해요.

마무리

한줄 정리하면, XOR reg, reg는 수학적 트릭이 아니라 CPU와 개발자 사이의 수십 년짜리 약속이에요. 여러분이 공부하면서 이런 어셈블리 관용구를 본 적 있나요? 혹시 비슷한 "왜 이렇게 쓰지?" 싶었던 저수준 코드 패턴이 있다면 댓글로 공유해주세요. 같이 뜯어보면 재미있을 것 같아요.


🔗 출처: Hacker News

이 뉴스가 유용했나요?

이 기술을 직접 배워보세요

AI 도구, 직접 활용해보세요

AI 시대, 코딩으로 수익을 만드는 방법을 배울 수 있습니다.

AI 활용 강의 보기

"비전공 직장인인데 반년 만에 수익 파이프라인을 여러 개 만들었습니다"

실제 수강생 후기
  • 비전공자도 6개월이면 첫 수익
  • 20년 경력 개발자 직강
  • 자동화 프로그램 + 소스코드 제공

매일 AI·개발 뉴스를 받아보세요

주요 테크 뉴스를 매일 아침 이메일로 전해드립니다.

스팸 없이, 언제든 구독 취소 가능합니다.