본문으로 건너뛰기

버그 제보

이상한 점을 알려 주시면 고칠게요. 지금 보고 있는 페이지와 브라우저 정보가 함께 담겨요.

페이지:

Computer Science

CPU와 명령어 구조

작성 2026. 10. 12.추가
목차16개 항목
출처와 참고

「반드시 알아야 할 레지스터」 절의 인터럽트를 넣은 명령어 사이클과 「RISC와 CISC」 절의 ISA 호환성 설명, 「소스 코드에서 명령어로」의 목적 코드와 「주소 지정 방식」의 비트 예시·방식별 비교는 강민철의 「혼자 공부하는 컴퓨터 구조+운영체제」 강의(한빛미디어)를 바탕으로 보강했다. 「명령어 길이」, 「점프와 반복」 절과 「상태 플래그」의 비교 방법, 「RISC와 CISC」의 ISA 확장 설명은 CrashCourse의 「Crash Course Computer Science」 강의를 바탕으로 보강했다. 「x86과 ARM」 절은 fru1tworld/cs-basic 모의 면접 질문 목록을 점검해 빠진 개념을 보강했다. 사이클별 마이크로 연산, 명령어 필드의 비트 배분, 변위 주소 지정 방식은 고려사이버대학교 「컴퓨터구조」(2024-1학기) 강의 내용으로 보강했다.

CPU가 실행하는 것은 결국 메모리에 놓인 기계어 명령어다. 명령어가 피연산자를 어디서 가져오는지, 실행 흐름이 어떻게 바뀌는지를 이해하면 컴파일러와 운영체제가 하는 일도 함께 보이기 시작한다.

소스 코드에서 명령어로

사람이 읽기 쉬운 고급 언어는 CPU가 직접 실행할 수 있는 저급 언어로 바뀌어야 실행된다. 저급 언어에는 0과 1로 된 기계어와, 기계어를 사람이 읽기 쉬운 기호로 옮긴 어셈블리어가 있다.

방식변환 단위특징예
컴파일소스 코드 전체를 컴파일러가 한 번에 목적 코드로 변환한 곳이라도 문법 오류가 있으면 실행되지 않는다C
인터프리트인터프리터가 한 줄씩 변환하며 실행N번째 줄에 오류가 있어도 N−1번째 줄까지는 실행된다. 보통 컴파일 언어보다 느리다Python

현대 언어는 두 방식을 섞어 쓰는 경우가 많아 경계가 모호하다. C의 컴파일 단계는 C 기초에서 다룬다.

컴파일 결과물인 저급 언어 코드를 목적 코드(object code)라 하고, 어셈블리어 한 줄 한 줄이 곧 명령어 하나다. 같은 소스라도 ISA와 컴파일러에 따라 명령어 열이 달라진다(아래 「RISC와 CISC」). Python·Java는 이 자리에 기계어 대신 VM이 해석할 바이트코드가 나온다(프로그램이 실행되기까지).

CPU 내부: ALU, 제어장치, 레지스터

부품받는 것내보내는 것
ALU레지스터의 피연산자, 제어장치의 "무슨 연산" 신호연산 결과(일단 레지스터로)와 플래그. 안에 가산기·보수기·시프터·오버플로우 검출기가 있다
제어장치클럭, 명령어 레지스터의 명령어, 플래그 값, 제어 버스로 들어온 신호(인터럽트 요청 등)CPU 안: ALU 연산·레지스터 이동 지시 / CPU 밖: 제어 버스로 메모리·입출력장치에 제어 신호

반드시 알아야 할 레지스터

프로그램 속 명령어와 데이터는 실행 전후로 반드시 레지스터를 거친다. 그래서 레지스터 값만 따라가도 실행 흐름이 보인다.

레지스터저장하는 것
프로그램 카운터(PC)메모리에서 다음에 읽을 명령어의 주소. 명령어 포인터라고도 한다
명령어 레지스터(IR)방금 읽어 들여 해석할 명령어
메모리 주소 레지스터(MAR)주소 버스로 내보낼 메모리 주소
메모리 버퍼 레지스터(MBR)데이터 버스로 주고받을 값. MDR(Memory Data Register)이라고도 한다
플래그 레지스터연산 결과나 CPU 상태에 대한 부가 정보
범용 레지스터데이터와 주소를 모두 담을 수 있는 자유로운 레지스터
스택 포인터메모리의 스택 영역에서 마지막으로 저장한 값의 위치
베이스 레지스터기준 주소. 변위 주소 지정에 쓴다

명령어 하나는 PC → MAR → (메모리 읽기) → MBR → IR 순으로 들어온다. PC는 그 사이 다음 주소로 늘고, 제어장치가 IR을 해석해 제어 신호를 낸다. 여기까지가 인출 사이클, 해석·실행이 실행 사이클이며, 간접 주소처럼 메모리에 한 번 더 가야 하면 간접 사이클이 끼어든다. 이 반복을 명령어 사이클이라 하고, 점프나 인터럽트가 있으면 순차 흐름이 끊긴다.

CPU는 실행 사이클이 끝날 때마다 다음 명령어를 인출하기 전에 인터럽트 요청을 확인한다. 그래서 인터럽트까지 넣은 명령어 사이클은 인출·간접·실행·인터럽트 네 사이클로 이뤄진다.

각 사이클은 클럭 한 주기에 하나씩 일어나는 마이크로 연산의 나열이다. 단일 누산기(AC) CPU에서 한 단계에 한 클럭씩 걸린다고 보면 다음과 같다.

사이클t0t1t2
인출MAR ← PCMBR ← M[MAR], PC ← PC + 1IR ← MBR
간접MAR ← IR(addr)MBR ← M[MAR]IR(addr) ← MBR (진짜 유효 주소로 바꿔 둔다)
실행: LOAD addrMAR ← IR(addr)MBR ← M[MAR]AC ← MBR
실행: STA addrMAR ← IR(addr)MBR ← ACM[MAR] ← MBR
실행: ADD addrMAR ← IR(addr)MBR ← M[MAR]AC ← AC + MBR
실행: JUMP addrPC ← IR(addr)--
인터럽트MBR ← PCMAR ← SP, PC ← ISR 시작 주소M[MAR] ← MBR (복귀 주소를 스택에 저장)

클럭이 1GHz(주기 1ns)면 인출 사이클만 3ns다. 점프가 PC에 값을 넣기만 하면 끝나는 이유도 표에서 보인다. 다음 인출 사이클이 그 주소에서 명령어를 가져오기 때문이다.

ISR을 실행하는 도중에 또 인터럽트가 오면(다중 인터럽트) 두 가지로 처리한다. 인터럽트 플래그를 꺼서 ISR이 끝날 때까지 새 요청을 받지 않거나(중단하면 안 되는 입출력 처리에 쓴다), 우선순위를 매겨 지금 것보다 높은 요청만 받아 ISR 안에서 다시 ISR로 들어간다(중첩).

flowchart LR
  ft["인출"] -->|"메모리 접근이 더 필요"| ind["간접"]
  ft -->|"바로 실행 가능"| ex["실행"]
  ind --> ex
  ex --> q{"인터럽트 요청?"}
  q -->|"없음 · 막혀 있음"| ft
  q -->|"있음"| it["인터럽트"]
  it --> ft

명령어 형식

ISA를 설계할 때는 네 가지를 정한다. 연산 종류(수·종류·복잡도), 데이터 형태(길이, 수 표현 방식), 명령어 형식(길이, 필드 수와 각 필드의 비트 수), 주소 지정 방식이다. 명령어 길이가 정해져 있으면 필드끼리 비트를 나눠 가지므로 하나를 늘리면 다른 것이 준다.

필드비트 수가 정하는 것16비트 명령어 예
연산 코드정의할 수 있는 연산의 수4비트면 16가지, 5비트로 늘리면 32가지지만 나머지 필드가 1비트 준다
오퍼랜드(주소)직접 가리킬 수 있는 메모리 범위연산 코드 5비트인 1주소 명령어는 주소 11비트 → 2¹¹ = 2048번지
오퍼랜드(레지스터 번호)쓸 수 있는 레지스터 수4비트면 16개
오퍼랜드(데이터)담을 수 있는 수의 범위12비트 2의 보수면 −2048 ~ +2047

주소 개수에 따른 분류

명령어에 피연산자 주소를 몇 개 적는지에 따라 나누며, CPU 구조(범용 레지스터·단일 누산기·스택)와 짝을 이룬다. 주소가 적을수록 명령어는 짧아지지만 필요한 명령어 개수는 늘어난다.

주소 수형식X = (A + B) * (C + D) 명령 수특징
3ADD R1, A, B3범용 레지스터 구조. 출발지 둘과 목적지를 모두 적는다. 프로그램이 짧지만 명령어가 길다
2ADD R1, B6 (MOV·ADD·MOV·ADD·MUL·MOV)첫 피연산자가 목적지를 겸한다. 가장 흔한 형식(x86)
1ADD B7 (LOAD·ADD·STORE T·LOAD·ADD·MUL·STORE)단일 누산기 구조. 누산기(AC)를 암묵적으로 쓰고, 중간 결과를 임시 위치에 둔다
0ADD8 (PUSH×4·ADD×2·MUL·POP)스택 구조. 스택 top 두 값을 꺼내 연산하고 결과를 push한다
; 0주소(스택)로 X = (A + B) * (C + D)
PUSH A
PUSH B
ADD          ; A+B
PUSH C
PUSH D
ADD          ; C+D
MUL          ; (A+B)*(C+D)
POP  X

RISC는 값을 먼저 레지스터로 LOAD하고 레지스터끼리만 연산한 뒤 STORE로 메모리에 쓴다.

명령어 길이: 고정 길이와 가변 길이

명령어 비트 수가 곧 표현력의 한계다. 8비트 명령어를 연산 코드 4비트 + 주소 4비트로 나누면 명령어는 최대 2⁴ = 16종류, 가리킬 수 있는 메모리는 16칸뿐이다. 17번지로 점프하는 명령어는 만들 수조차 없다. 이 한계를 푸는 방법은 두 가지다.

방법동작예
고정 길이를 늘린다모든 명령어를 32비트 등으로 키워 연산 코드와 주소 필드를 넓힌다. 모든 명령어의 경계가 일정해 인출·해독이 단순하다ARM64(A64)·RISC-V 기본 명령어는 32비트 고정
가변 길이연산 코드를 먼저 읽고, 그 연산이 값을 더 필요로 하면 바로 뒤 바이트를 이어서 읽는다. 뒤에 붙은 값을 즉시값(immediate value)이라 한다. HALT처럼 추가 값이 없는 명령어는 짧게 끝난다x86-64는 명령어 하나가 1~15바이트

가변 길이는 자주 쓰는 명령어를 짧게 만들어 메모리를 아끼지만, 다음 명령어가 어디서 시작하는지 해독해 봐야 알 수 있어 인출·해독 단계가 복잡해진다. 이 차이가 아래 RISC와 CISC를 가르는 축 중 하나다.

주소 지정 방식 (Addressing Mode)

같은 명령어라도 피연산자를 찾는 방법을 다양하게 두면 포인터·배열·카운터를 표현하기 쉽고, 프로그램을 메모리 어디에 두어도(재배치) 동작하게 만들 수 있으며, 주소 필드의 비트 수도 줄일 수 있다.

방식피연산자 위치 / 유효 주소(EA)
묵시적(Implied)명령어에 암묵적으로 정해짐 (AC, 스택 top)
즉시(Immediate)명령어 안에 값 자체가 있음. 상수 초기화에 사용
레지스터CPU 레지스터 안에 피연산자
레지스터 간접레지스터가 피연산자의 주소를 가짐
자동 증가/감소레지스터 간접 후 레지스터 값을 자동으로 ±1
직접주소 필드가 곧 EA
간접주소 필드가 가리키는 곳에 EA가 저장됨
상대EA = 주소 필드 + PC (분기에 유용)
인덱스EA = 주소 필드 + 인덱스 레지스터 (배열 원소)
베이스 레지스터EA = 주소 필드 + 베이스 레지스터 (프로그램 재배치)
스택스택 포인터가 가리키는 스택 top
  • 오퍼랜드 필드에는 값보다 값이 있는 위치를 적는 경우가 많다. 그래서 오퍼랜드 필드를 주소 필드라고도 부른다. 명령어 길이가 정해져 있어 값을 직접 넣으면 표현 범위가 작아지기 때문이다. 16비트 명령어에서 연산 코드가 4비트라면, 오퍼랜드가 2개일 때 하나에 6비트(64가지), 3개일 때 4비트(16가지)밖에 남지 않는다. 대신 16비트 메모리 칸의 주소를 적으면 그 칸에 2¹⁶가지 값을 담을 수 있다.
  • 연산에 쓸 데이터가 실제로 있는 위치를 유효 주소(EA)라 하고, 주소 지정 방식은 곧 유효 주소를 찾는 방법이다.
  • 속도와 표현 범위는 맞바꾼다. 기본 다섯 방식을 비교하면 다음과 같다.
기본 방식오퍼랜드 필드에 적는 것데이터까지 메모리 접근맞바꾸는 것
즉시데이터 자체0번가장 빠르지만 담을 수 있는 값이 작다
직접유효 주소1번주소 범위가 연산 코드 비트만큼 줄어든다
간접유효 주소의 주소2번주소 범위는 넓지만 느리다
레지스터데이터가 든 레지스터0번직접 방식보다 빠르지만 레지스터 수가 적다
레지스터 간접유효 주소가 든 레지스터1번간접 방식보다 메모리 접근이 한 번 적다

CPU 밖에 있는 메모리에 가는 것은 CPU 안의 레지스터를 읽는 것보다 훨씬 느리다. 그래서 메모리 접근 횟수가 속도를 가른다.

  • 간접 방식과 레지스터 간접 방식이 넓은 범위를 얻는 이유는 주소가 명령어 필드가 아니라 워드 하나(또는 레지스터 하나) 전체에 담기기 때문이다. 워드가 n비트면 2ⁿ곳을 가리킨다. 16비트 레지스터면 64K워드, 32비트면 4G워드다. 간접 여부는 명령어의 간접 비트(I)로 구분하고(I = 1이면 간접 사이클을 거친다), 주소의 주소를 여러 번 따라가는 다단계 간접 방식도 있다.
  • 상대·인덱스·베이스 레지스터 방식은 모두 "레지스터 값 + 변위"로 주소를 구해 변위 주소 지정(EA = A + (R))이라 묶는다. 어떤 레지스터를 더하느냐만 다르다.
변위 방식더하는 레지스터주 용도와 특징
상대PC분기. 변위 A를 2의 보수로 두어 양수면 앞으로, 음수면 뒤로 간다. 전체 주소 대신 짧은 변위만 적어 비트를 아끼지만, 분기 범위가 변위 필드의 2의 보수 범위로 묶인다
인덱스인덱스 레지스터(IX)배열 원소 접근. 자동 인덱싱이면 명령어를 실행할 때마다 IX가 1씩 늘거나 줄어 루프에서 따로 증가 명령이 필요 없다
베이스 레지스터베이스 레지스터(BR)프로그램 재배치. 다중 프로그래밍에서 프로그램을 다른 위치로 옮겨도 명령어의 주소 필드는 그대로 두고 BR만 바꾸면 된다. 베이스 레지스터가 200, 오퍼랜드가 40이면 240번지다

x86의 세그먼트 방식도 같은 생각이다. 명령어로 구한 유효 주소에 세그먼트 레지스터가 가리키는 세그먼트 시작 주소를 더해 선형 주소를 만든다.

연산 코드의 종류

명령어는 연산 코드(무엇을 할지)와 오퍼랜드(무엇으로 할지)로 이뤄진다. 연산 코드의 생김새는 CPU마다 다르지만 대부분의 CPU가 공통으로 가진 유형은 네 가지다.

유형대표 명령어
데이터 전송MOVE, LOAD(메모리 → CPU), STORE(CPU → 메모리), EXCHANGE, PUSH, POP
산술·논리 연산ADD, SUBTRACT, MULTIPLY, DIVIDE, INCREMENT, DECREMENT, AND, OR, NOT, COMPARE, 시프트
제어 흐름 변경JUMP, CONDITIONAL JUMP, HALT, CALL(복귀 주소를 저장하고 이동), RETURN
입출력 제어READ(INPUT), WRITE(OUTPUT), START IO, TEST IO

프로그램 제어

상태 플래그

플래그의미
C (Carry)ALU 출력의 캐리
S (Sign)결과의 부호 비트
Z (Zero)결과가 0인지
V (Overflow)오버플로우 여부 (마지막 두 캐리의 XOR)
인터럽트 플래그하드웨어 인터럽트를 받아들일 수 있는지
슈퍼바이저 플래그커널 모드인지 사용자 모드인지 (모드 비트)

예: A = 11110000, B = 00010100일 때 A − B를 2의 보수 덧셈으로 계산하면

      A : 11110000
 B'+1   : 11101100
  A - B : 11011100   → C=1, S=1, Z=0, V=0
  • Z 플래그 회로는 단순하다. 결과의 모든 비트를 OR 게이트로 모아 하나라도 1인지 보고, 그 출력을 NOT으로 뒤집는다. 모든 비트가 0일 때만 1이 된다.
  • 두 수의 비교는 별도 회로 없이 뺄셈 후 플래그 확인으로 한다. A − B를 계산해 Z가 1이면 A = B, 결과가 음수(S가 1)면 A < B다. 부호 있는 수는 뺄셈이 오버플로우하면 S가 뒤집히므로 정확히는 S ⊕ V로 판단한다. x86의 CMP, ARM의 CMP가 결과는 버리고 플래그만 남기는 뺄셈이다.

조건부 분기 명령어는 이 플래그 값을 보고 점프 여부를 결정한다.

점프와 반복

점프는 특별한 회로가 아니라 PC에 새 값을 덮어쓰는 것이다. 다음 인출이 그 주소에서 일어나므로 흐름이 바뀐다.

명령어동작
JUMP n무조건 PC ← n
JUMP_NEGATIVE n 등 조건부 점프지정한 플래그(음수, 0, 크다 …)가 켜져 있을 때만 PC ← n, 아니면 다음 명령어로 진행
HALT실행을 멈춘다

무조건 점프로 뒤로 돌아가기만 하면 영원히 끝나지 않는 무한 루프가 된다. 루프를 빠져나오려면 조건부 점프가 있어야 한다. 아래는 나눗셈 회로가 없는 CPU에서 11을 5로 나눈 나머지를 뺄셈 반복으로 구하는 프로그램이다.

0: LOAD_A 14          ; A ← 11
1: LOAD_B 15          ; B ← 5
2: SUB B A            ; A ← A − B
3: JUMP_NEGATIVE 5    ; 음수가 되면 루프 탈출
4: JUMP 2             ; 아니면 다시 빼기
5: ADD B A            ; 하나 더 뺀 만큼 되돌림 → A = 1 (나머지)
6: STORE_A 13
7: HALT

여덟 줄짜리 프로그램이지만 2번 줄로 두 번 되돌아가 실제로는 13개의 명령어가 실행된다. 루프 횟수를 세면 몫도 얻는다. 하드웨어에 없는 기능(나눗셈)을 소프트웨어가 만들어 낸 것이고, 이 프로그램을 다른 프로그램이 다시 부품처럼 쓸 수 있다.

HALT가 중요한 이유는 명령어와 데이터가 같은 메모리에 같은 2진수로 놓이기 때문이다. 멈추라는 명령어가 없으면 CPU는 프로그램 끝 다음 칸의 데이터를 명령어로 읽어 계속 실행한다.

서브루틴 호출과 인터럽트

CALL:   SP ← SP - 1;  M[SP] ← PC;  PC ← 유효 주소     ; 복귀 주소 push 후 이동
RETURN: PC ← M[SP];  SP ← SP + 1                     ; 복귀 주소 pop

복귀 주소를 스택에 두므로 재귀 호출도 복귀 주소를 덮어쓰지 않는다. 메모리가 바이트 단위로 주소를 매기고 복귀 주소가 16비트라면 SP는 1이 아니라 2씩 움직인다. 인터럽트도 흐름을 다른 루틴으로 옮겼다가 돌아오지만 다음이 다르다.

항목서브루틴 호출인터럽트
흐름을 바꾸는 것프로그램 안의 CALL 명령내·외부 신호
루틴 주소명령어의 오퍼랜드하드웨어(인터럽트 벡터)가 정한다
저장하는 것PC(복귀 주소)CPU 상태 전체. 이 묶음이 PSW(Program Status Word)로, PC·레지스터·상태 플래그를 포함한다

인터럽트는 원인에 따라 외부(하드웨어), 내부(트랩·예외), 소프트웨어(시스템 콜)로 나뉜다. 분류표는 컴퓨터 시스템 구조와 인터럽트에, 하드웨어가 인터럽트를 받아 ISR을 실행하고 복귀하는 처리 순서와 우선순위는 입출력 구조에 있다. 하드웨어 인터럽트 덕분에 CPU는 느린 장치를 기다리지 않고 다른 일을 하다가 "끝났다"는 알림만 받으면 된다.

RISC와 CISC

CPU가 이해하는 명령어의 모음을 명령어 집합 구조(ISA, Instruction Set Architecture)라고 한다. 인텔·AMD의 PC CPU는 x86(x86-64) ISA를, 아이폰과 애플 실리콘의 CPU는 ARM ISA를 이해한다. CPU는 자기 ISA에 속한 명령어만 이해한다. 같은 C 소스를 컴파일해도 x86-64용과 ARM64용 어셈블리가 다르게 나오고, x86-64용 실행 파일을 ARM 컴퓨터로 옮기면 그대로는 실행되지 않는다. 애플의 Rosetta 2 같은 변환 계층이나 다시 컴파일한 바이너리가 있어야 한다. ISA는 CPU의 언어인 셈이라, ISA가 다르면 명령어 해석 방식, 레지스터 종류와 개수, 메모리 관리 방법, 하드웨어 설계가 모두 달라진다. 현대 ISA는 크게 CISC(x86)와 RISC(ARM)로 나뉜다.

CISC는 복잡한 가변 길이 명령어로 적은 수의 명령어만으로 프로그램을 돌리고 메모리를 아낀다. 하지만 명령어마다 길이와 수행 시간이 제각각이라 파이프라인에 넣기 어렵고, 복잡한 명령어 대부분은 잘 쓰이지 않았다. 여기서 "명령어를 짧고 규격화해 파이프라인을 살리고, 자주 쓰는 기본 명령어를 작고 빠르게 만든다"는 RISC(Reduced Instruction Set Computer)가 1980년경 UC 버클리에서 나왔다. MIPS, SPARC, PowerPC, 그리고 오늘날의 ARM, RISC-V가 RISC다.

CISCRISC
명령어 수·주소 지정 방식이 많고 일부는 거의 안 쓰임명령어 수·주소 지정 방식이 적음
가변 길이 명령어고정 길이 명령어 → 디코딩이 단순
메모리의 피연산자를 직접 연산메모리 접근은 load/store로만, 연산은 레지스터끼리
마이크로프로그램 제어, 여러 클럭에 걸쳐 실행 → 파이프라이닝 어려움하드와이어드 제어, 1클럭 내외 → 파이프라이닝 쉬움
프로그램을 이루는 명령어 수가 적음프로그램을 이루는 명령어 수가 많음

하드와이어드 제어와 마이크로프로그램 제어의 차이는 기본 컴퓨터의 구조와 설계에 있다. RISC는 제어 장치가 단순해 남는 칩 면적에 레지스터를 많이 두고, 파이프라인을 짜기 쉬우며, 성능의 큰 몫을 컴파일러 최적화에 맡긴다.

ISA는 시간이 갈수록 커진다. 범용 명령어로 많은 클럭이 걸리는 일에 전용 회로와 전용 명령어를 더해 왔기 때문이다. x86에는 하드웨어 나눗셈에 이어 SIMD 확장(MMX → SSE → AVX)과 AES 암호화 명령어(AES-NI)가 붙었다. 한번 들어간 명령어는 그것을 쓰는 프로그램이 있어 하위 호환을 위해 빼지 못한다. 그래서 x86-64는 명령어와 그 변형이 수천 개에 이른다.

이 표는 설계 철학의 비교다. 현대 x86 CPU는 CISC 명령어를 내부에서 RISC 같은 μop으로 쪼갠 뒤 파이프라인과 비순차 실행으로 처리하므로, 칩 내부 동작만 보면 둘의 차이는 크지 않다(현대 CPU의 마이크로아키텍처).

x86과 ARM

비교x86-64 (Intel, AMD)ARM64 (AArch64)
명령어CISC, 가변 길이(1~15바이트)RISC, 고정 32비트
범용 레지스터16개31개
메모리 순서 모델강함(TSO). 저장끼리, 읽기끼리의 순서가 다른 코어에도 유지된다약함. 배리어 없이는 다른 코어에 보이는 순서가 바뀔 수 있다
SIMDSSE, AVX, AVX-512NEON, SVE
SMT대부분 지원(하이퍼스레딩)Graviton·Ampere 같은 서버 칩은 코어당 스레드 하나
사업 모델Intel·AMD가 설계하고 판다ARM이 ISA와 코어 설계를 라이선스하고 Apple·AWS·Qualcomm 등이 자기 칩을 만든다
강점높은 단일 코어 성능, 수십 년 쌓인 소프트웨어 호환성전력 대비 성능, 용도에 맞춘 칩 설계

메모리 순서 모델의 차이는 이식할 때 드러난다. x86에서 우연히 맞게 돌던 락 없는 코드가 ARM에서 깨질 수 있다(POSIX 스레드와 동기화 API의 「가시성·순서와 volatile」).

클라우드에서의 차이. x86 인스턴스의 vCPU는 보통 하이퍼스레드 하나, 즉 물리 코어의 절반이다. AWS Graviton 같은 ARM 인스턴스의 vCPU는 물리 코어 하나라, 같은 vCPU 수에서 옆 스레드와 실행 유닛을 나눠 쓰지 않아 성능이 고르다. AWS는 Graviton 인스턴스가 비슷한 x86 인스턴스보다 최대 20% 싸고, 같은 성능에서 비슷한 EC2 인스턴스보다 에너지를 최대 60% 덜 쓴다고 밝힌다(AWS Graviton, 벤더 발표 수치). 대신 x86 전용 바이너리나 AVX-512에 기댄 라이브러리는 다시 빌드하거나 대체해야 하고, 컨테이너 이미지는 linux/amd64와 linux/arm64를 함께 빌드(multi-arch)한다. JVM·인터프리터 위에서 도는 코드는 대개 그대로 옮겨진다. 그 밖에 클라우드 VM은 물리 서버의 캐시와 메모리 대역폭을 다른 고객과 나눠 써서 이웃 부하에 성능이 흔들리고(noisy neighbor), 큰 인스턴스는 여러 NUMA 노드에 걸치며(메모리 계층 구조), 버스트형 인스턴스(AWS T 계열)는 CPU 크레딧을 다 쓰면 기준 성능으로 묶인다. 가상화 자체는 가상화에 있다.

서버 CPU의 방향. 클럭 대신 코어 수를 늘린다. 작은 다이 여러 개(칩렛)를 한 패키지에 묶어 수율을 지키며 코어를 늘리고(AMD EPYC), 행렬 곱(Intel AMX)·암호화·압축 같은 자주 쓰는 일에 전용 명령어와 가속기를 넣고, 메모리 채널을 늘리고 CXL 같은 인터커넥트로 메모리를 확장해 코어 수에 맞는 대역폭을 댄다.

중첩 레지스터 윈도우

프로시저 호출마다 메모리 스택으로 인자를 주고받으면 느리다. Berkeley RISC I은 많은 레지스터를 여러 "윈도우"로 나누고, 호출하는 쪽과 호출되는 쪽의 윈도우가 일부 겹치도록 했다. 겹친 레지스터에 인자를 두면 복사 없이 바로 전달되어 호출·복귀가 빨라진다.

핵심 정리

  • CPU는 PC → MAR → MBR → IR 순으로 명령어를 인출하고, 제어장치가 해석해 실행하는 명령어 사이클을 반복한다.
  • 주소 개수가 적을수록 명령어는 짧고, 대신 명령어 수가 늘어난다.
  • 주소 지정 방식은 포인터·배열·재배치를 지원하기 위한 장치다.
  • 점프는 PC 덮어쓰기이고, 조건부 점프가 플래그를 읽어 루프와 분기를 만든다. 비교는 뺄셈 후 플래그 확인이다.
  • 인터럽트는 외부 신호로 흐름이 바뀌므로 PC만이 아니라 PSW 전체를 저장한다.
  • RISC는 단순한 고정 길이 명령어 + load/store 구조로 파이프라인 효율을 높인다. 현대 x86도 내부에서는 μop으로 바꿔 RISC처럼 실행한다.

관련 노트: 현대 CPU의 마이크로아키텍처, 파이프라인과 병렬 처리, 입출력 구조, 기본 컴퓨터 프로그래밍

원본 기록12

댓글

GitHub 계정으로 댓글을 남길 수 있어요.