📚 이전 편 복습
[0] 기초편에서 우리는 컴퓨터의 5가지 핵심 구성 요소(CPU, 메모리, 입력장치, 출력장치, 시스템 버스)와 폰 노이만 구조를 배웠습니다. 이번 편에서는 그 중 가장 중요한 CPU의 내부 구조를 깊이 있게 파헤쳐 보겠습니다.
📑 목차
⚡ CPU란 무엇인가? - 컴퓨터의 두뇌
CPU(Central Processing Unit, 중앙처리장치)는 컴퓨터에서 가장 중요한 부품입니다. 모든 연산과 제어를 담당하는 "컴퓨터의 두뇌"라고 불립니다.
CPU가 하는 일
- 명령어 해석: 프로그램의 명령어를 읽고 이해
- 연산 수행: 산술 연산(+, -, ×, ÷)과 논리 연산(AND, OR, NOT) 처리
- 데이터 이동: 메모리와 레지스터 사이에서 데이터 전송
- 흐름 제어: 프로그램 실행 순서 결정
💡 실무 예시
Delphi로 설비 모니터링 프로그램을 만들 때, if 문으로 센서 값을 체크하는 코드를 작성하면 CPU는 이를 비교 연산과 조건 분기 명령어로 변환해서 실행합니다.
if SensorValue > Threshold then
AlarmOn := True; // CPU가 비교 연산 → 분기 → 대입 순서로 처리
CPU의 성능 지표
| 성능 지표 | 설명 | 단위 |
|---|---|---|
| 클럭 속도 | 1초에 수행하는 사이클 수 | GHz (기가헤르츠) |
| 코어 수 | 동시 작업 가능한 CPU 개수 | 개 (예: 8코어) |
| 캐시 크기 | CPU 내부 초고속 메모리 | MB (메가바이트) |
| IPC | 클럭당 처리 명령어 수 | 명령어/클럭 |
🔧 CPU의 3대 핵심 구성 요소
CPU는 크게 제어장치, 연산장치, 레지스터 세 부분으로 구성됩니다.
CPU 내부 구조도
┌─────────────────────────────────────────────┐
│ CPU │
│ │
│ ┌──────────────────┐ ┌─────────────────┐ │
│ │ 제어장치 (CU) │ │ 연산장치 (ALU) │ │
│ │ │ │ │ │
│ │ • 명령어 해석 │ │ • 산술 연산 │ │
│ │ • 제어 신호 발생 │ │ • 논리 연산 │ │
│ │ • 실행 순서 결정 │ │ • 비교 연산 │ │
│ └──────────────────┘ └─────────────────┘ │
│ ↕ ↕ │
│ ┌─────────────────────────────────────┐ │
│ │ 레지스터 (Registers) │ │
│ │ PC | IR | AC | MAR | MBR | SR ... │ │
│ └─────────────────────────────────────┘ │
│ ↕ │
└─────────────────────┼───────────────────────┘
↕
[시스템 버스]
↕
[메모리]
1. 제어장치 (Control Unit, CU)
CPU의 지휘자 역할을 합니다. 명령어를 해석하고 각 구성 요소에게 무엇을 해야 할지 지시합니다.
주요 기능:
- 명령어 인출: 메모리에서 다음 실행할 명령어 가져오기
- 명령어 해독: 명령어가 무엇을 의미하는지 분석
- 제어 신호 생성: ALU, 레지스터, 메모리에 동작 신호 전달
- 프로그램 카운터 관리: 다음 실행할 명령어 주소 추적
2. 연산장치 (Arithmetic Logic Unit, ALU)
실제 계산을 수행하는 부분입니다. 모든 수학적 연산과 논리 판단을 담당합니다.
수행하는 연산:
산술 연산 (Arithmetic Operations)
- 덧셈 (ADD): A + B
- 뺄셈 (SUB): A - B
- 곱셈 (MUL): A × B
- 나눗셈 (DIV): A ÷ B
논리 연산 (Logical Operations)
- AND: 두 비트가 모두 1일 때만 1
- OR: 두 비트 중 하나라도 1이면 1
- NOT: 비트 반전 (0↔1)
- XOR: 두 비트가 다르면 1
비교 연산 (Comparison)
- 크다 (>), 작다 (<), 같다 (=)
- 결과를 플래그 레지스터에 저장
🔍 실무 활용
설비 모니터링에서 센서 값을 비교할 때:
// Delphi 코드
if (SensorA > 100) and (SensorB < 50) then
ShowAlarm;
// CPU는 이렇게 처리:
// 1. SensorA와 100을 ALU에서 비교 (CMP 명령어)
// 2. SensorB와 50을 ALU에서 비교
// 3. 두 결과를 AND 논리 연산
// 4. 결과가 True면 ShowAlarm으로 분기
3. 레지스터 (Registers)
CPU 내부의 초고속 임시 저장 공간입니다. RAM보다 100배 이상 빠릅니다.
특징:
- 크기: 일반적으로 32비트 또는 64비트
- 속도: 0.1 나노초 수준 (RAM의 1/100)
- 개수: CPU마다 다르지만 보통 수십 개
- 용도: 데이터, 주소, 제어 정보 저장
💾 레지스터의 종류와 역할
CPU에는 여러 종류의 레지스터가 있으며, 각각 특별한 역할을 담당합니다.
범용 레지스터 (General Purpose Registers)
- 데이터 레지스터: 연산에 사용할 데이터 임시 저장
- 주소 레지스터: 메모리 주소 계산에 사용
- 누산기(AC): ALU 연산 결과를 저장하는 특수 레지스터
특수 목적 레지스터 (Special Purpose Registers)
| 레지스터 | 이름 | 역할 |
|---|---|---|
| PC | Program Counter | 다음 실행할 명령어의 주소 저장 |
| IR | Instruction Register | 현재 실행 중인 명령어 저장 |
| MAR | Memory Address Register | 접근할 메모리 주소 저장 |
| MBR | Memory Buffer Register | 메모리에서 읽거나 쓸 데이터 저장 |
| SR | Status Register | 연산 결과의 상태 플래그 저장 |
상태 레지스터 (Status Register) 플래그
상태 레지스터는 여러 개의 플래그(Flag)로 구성됩니다:
- Zero Flag (Z): 연산 결과가 0이면 1
- Carry Flag (C): 자리 올림이 발생하면 1
- Overflow Flag (V): 오버플로우 발생 시 1
- Negative Flag (N): 연산 결과가 음수이면 1
이 플래그들은 조건 분기 명령어(if, while)를 실행할 때 사용됩니다.
🔄 명령어 사이클 - Fetch, Decode, Execute
CPU는 명령어 사이클(Instruction Cycle)이라는 반복 과정을 통해 프로그램을 실행합니다.
3단계 명령어 사이클
인출 (Fetch)
메모리에서 다음 실행할 명령어를 가져옵니다.
- PC(프로그램 카운터)가 가리키는 주소를 MAR에 저장
- 해당 주소의 명령어를 MBR로 읽어옴
- MBR의 내용을 IR(명령어 레지스터)에 저장
- PC 값을 1 증가 (다음 명령어 준비)
해독 (Decode)
명령어가 무엇을 의미하는지 분석합니다.
- 제어장치가 IR의 명령어를 해석
- 어떤 연산을 해야 하는지 파악
- 필요한 데이터의 위치 확인
- 각 구성 요소에 제어 신호 준비
실행 (Execute)
해독된 명령어를 실제로 수행합니다.
- ALU에서 필요한 연산 수행
- 메모리 읽기/쓰기 작업 실행
- 레지스터 간 데이터 이동
- 결과를 적절한 위치에 저장
반복
프로그램이 끝날 때까지 1→2→3을 계속 반복합니다.
명령어 사이클 상세 예시
예제: C = A + B 연산을 CPU가 처리하는 과정
메모리 상태:
주소 100: LOAD A // A 값을 레지스터로 로드
주소 101: ADD B // B 값을 더함
주소 102: STORE C // 결과를 C에 저장
1단계: Fetch (주소 100)
PC = 100 → MAR
메모리[100] → MBR
MBR → IR ("LOAD A")
PC = PC + 1 (101)
2단계: Decode
제어장치: "LOAD 명령어 → 메모리에서 데이터 읽기"
A의 주소 확인
3단계: Execute
메모리에서 A 값을 읽어 누산기(AC)에 저장
AC = A
[다음 사이클로 반복]
1단계: Fetch (주소 101)
PC = 101 → MAR
메모리[101] → MBR ("ADD B")
PC = 102
2단계: Decode
"ADD 명령어 → ALU에서 덧셈"
B의 주소 확인
3단계: Execute
ALU: AC + B 계산
AC = A + B
[다음 사이클]
1단계: Fetch (주소 102)
"STORE C"
PC = 103
2단계: Decode
"STORE → 메모리에 쓰기"
3단계: Execute
AC 값을 메모리 C 위치에 저장
C = A + B (완료!)
⏱️ 클럭(Clock)과 처리 속도의 비밀
클럭(Clock)은 CPU의 심장박동과 같습니다. CPU의 모든 동작은 클럭 신호에 맞춰 동기화됩니다.
클럭이란?
일정한 간격으로 발생하는 전기적 펄스 신호입니다. 이 신호에 맞춰 CPU의 모든 회로가 동작합니다.
클럭 신호 파형
전압
↑
│ ┌─┐ ┌─┐ ┌─┐ ┌─┐
│ │ │ │ │ │ │ │ │
│──┘ └──┘ └──┘ └──┘ └──→ 시간
│ ← 1클럭 사이클 →
└─────────────────────→
클럭 속도 (Clock Speed)
클럭 속도는 1초에 발생하는 클럭 사이클 수를 의미하며, Hz(헤르츠) 단위로 표현합니다.
예시:
- 3.5 GHz CPU: 1초에 35억 번 클럭 발생
- 1클럭 = 약 0.286 나노초 (ns)
- 매우 짧은 시간에 명령어 처리 가능
CPI와 IPC
| 용어 | 의미 | 설명 |
|---|---|---|
| CPI | Cycles Per Instruction | 명령어 하나를 실행하는데 필요한 클럭 사이클 수 |
| IPC | Instructions Per Cycle | 1클럭에 처리할 수 있는 명령어 수 (CPI의 역수) |
⚠️ 클럭 속도만으로는 성능을 판단할 수 없다!
2000년대 초반, 인텔은 Pentium 4를 3.8GHz까지 올렸지만, AMD의 2.4GHz CPU보다 느린 경우가 많았습니다. 이유는 IPC와 아키텍처의 차이 때문입니다.
현대 CPU는 클럭 속도보다 효율성과 병렬 처리를 중시합니다.
실무 팁: 코어 수 vs 클럭 속도
멀티스레드 작업 (예: 영상 편집, 3D 렌더링, 대용량 데이터 처리)
- → 코어 수가 많은 CPU 선택 (8코어, 16코어)
싱글스레드 작업 (예: 게임, 일부 시뮬레이션, 실시간 제어)
- → 클럭 속도가 높은 CPU 선택 (4.5GHz 이상)
설비 모니터링 (실시간 데이터 처리)
- → 클럭 속도 우선, 안정성 중시 (보통 클럭 CPU로 충분)
🚀 파이프라이닝 - CPU 성능 향상 기법
파이프라이닝(Pipelining)은 명령어 처리 과정을 여러 단계로 나누어 동시에 여러 명령어를 처리하는 기법입니다.
파이프라이닝 없이 (순차 처리)
시간 →
명령어1: [Fetch] [Decode] [Execute]
명령어2: [Fetch] [Decode] [Execute]
명령어3: [Fetch] [Decode] [Execute]
총 시간: 9 클럭
각 명령어가 완전히 끝나야 다음 명령어를 시작합니다.
파이프라이닝 적용 (병렬 처리)
시간 →
명령어1: [Fetch] [Decode] [Execute]
명령어2: [Fetch] [Decode] [Execute]
명령어3: [Fetch] [Decode] [Execute]
총 시간: 5 클럭 (44% 시간 단축!)
한 명령어가 Decode 단계에 있을 때, 다음 명령어는 Fetch를 시작합니다.
파이프라이닝 단계
현대 CPU는 보통 5단계 파이프라인을 사용합니다:
- IF (Instruction Fetch): 명령어 인출
- ID (Instruction Decode): 명령어 해독
- EX (Execute): 실행
- MEM (Memory Access): 메모리 접근
- WB (Write Back): 결과 저장
5단계 파이프라인 동작
클럭 1 2 3 4 5 6 7 8
명령1 [IF][ID][EX][MEM][WB]
명령2 [IF][ID][EX][MEM][WB]
명령3 [IF][ID][EX][MEM][WB]
명령4 [IF][ID][EX][MEM][WB]
명령5 [IF][ID][EX][MEM]
이상적으로는 매 클럭마다 1개의 명령어 완료!
파이프라인 해저드 (Pipeline Hazard)
파이프라이닝이 항상 완벽하게 작동하는 것은 아닙니다. 다음과 같은 해저드(위험)가 발생할 수 있습니다:
1. 데이터 해저드 (Data Hazard)
이전 명령어의 결과를 다음 명령어가 사용해야 할 때 발생
명령1: ADD R1, R2, R3 // R1 = R2 + R3
명령2: SUB R4, R1, R5 // R4 = R1 - R5 (R1이 아직 준비 안됨!)
해결: Forwarding (결과를 미리 전달) 또는 Stall (대기)
2. 제어 해저드 (Control Hazard)
분기 명령어(if, goto)로 인해 다음 명령어를 예측할 수 없을 때
명령1: CMP R1, R2 // R1과 R2 비교
명령2: JEQ Target // 같으면 Target으로 점프
명령3: ADD ... // 이 명령어를 실행해야 할지 알 수 없음
해결: Branch Prediction (분기 예측)
3. 구조적 해저드 (Structural Hazard)
하드웨어 자원 부족으로 여러 명령어가 동시에 같은 자원을 사용하려 할 때
분기 예측 (Branch Prediction)
현대 CPU는 분기 예측기를 사용해 제어 해저드를 최소화합니다:
- 정적 예측: 항상 "분기함" 또는 "분기 안함"으로 예측
- 동적 예측: 과거 분기 이력을 학습해서 예측 (정확도 95% 이상)
예측이 틀리면 파이프라인을 비워야 하므로(Flush), 예측 정확도가 매우 중요합니다.
슈퍼스칼라 (Superscalar)
슈퍼스칼라는 여러 개의 파이프라인을 병렬로 운영해서 한 클럭에 여러 명령어를 동시 실행하는 기법입니다.
예시: 4-Way 슈퍼스칼라
- 4개의 명령어를 동시에 처리
- 이론적으로 IPC = 4 (클럭당 4개 명령어)
- Intel Core i7, AMD Ryzen 등 대부분의 현대 CPU가 슈퍼스칼라 구조
💼 실무 적용 사례
사례 1: 설비 모니터링 프로그램 최적화
문제 상황: 1초마다 16채널 센서 데이터를 수집하는데, CPU 사용률이 80%까지 올라감
원인 분석:
- 모든 센서를 순차적으로 읽어서 비효율적
- 분기문(
if)이 많아 파이프라인 효율 저하
해결 방법:
// 개선 전 (순차 처리)
for i := 0 to 15 do
begin
SensorData[i] := ReadSensor(i); // 매번 함수 호출
if SensorData[i] > Threshold then
ProcessAlarm(i); // 분기 발생
end;
// 개선 후 (배치 처리 + 분기 최소화)
// 1. 모든 센서 값을 한번에 읽기 (DMA 사용)
ReadAllSensors(@SensorData);
// 2. 분기 없이 플래그만 설정
for i := 0 to 15 do
AlarmFlags[i] := SensorData[i] > Threshold;
// 3. 알람 처리를 별도 스레드로 분리
if HasAlarm(AlarmFlags) then
TriggerAlarmThread;
결과: CPU 사용률 30%로 감소!
사례 2: 캐시 친화적 코드 작성
CPU는 메모리 접근 시 캐시(Cache)를 사용합니다. 캐시 히트율을 높이면 성능이 크게 향상됩니다.
// 나쁜 예: 캐시 미스 많이 발생
type
TLogData = record
Time: TDateTime;
Ch01: Double;
Ch02: Double;
// ... Ch16까지
end;
// 각 채널별로 통계 계산 → 메모리 점프가 많음
for ch := 1 to 16 do
for i := 0 to 999 do
Sum[ch] := Sum[ch] + LogData[i].Channels[ch];
// 좋은 예: 순차 접근으로 캐시 효율 향상
// 시간 순서대로 모든 채널 처리
for i := 0 to 999 do
for ch := 1 to 16 do
Sum[ch] := Sum[ch] + LogData[i].Channels[ch];
성능 차이: 약 3~5배 향상 (캐시 덕분)
사례 3: 루프 언롤링 (Loop Unrolling)
반복문의 오버헤드를 줄이고 파이프라인 효율을 높이는 기법
// 일반 루프 (분기 명령어 1000번)
for i := 0 to 999 do
Sum := Sum + Data[i];
// 언롤링 (분기 명령어 250번)
for i := 0 to 249 do
begin
Sum := Sum + Data[i*4];
Sum := Sum + Data[i*4+1];
Sum := Sum + Data[i*4+2];
Sum := Sum + Data[i*4+3];
end;
장점: 분기 예측 미스 감소, ILP(Instruction Level Parallelism) 향상
주의: 코드 크기 증가, 과도한 언롤링은 캐시 미스 유발 가능
🚀 다음 편 예고: 컴퓨터 구조 [2] - 메모리 계층 구조
다음 포스팅에서는 메모리 시스템을 깊이 있게 다룰 예정입니다.
다룰 내용:
- 메모리 계층 구조 (레지스터 → 캐시 → RAM → 디스크)
- 캐시 메모리의 동작 원리와 매핑 기법
- 가상 메모리와 페이징
- 메모리 접근 속도 최적화 기법
- 설비 모니터링에서의 메모리 관리 전략
📌 핵심 요약
- CPU는 제어장치, 연산장치, 레지스터 3가지 핵심 구성으로 이루어짐
- 명령어 사이클은 Fetch → Decode → Execute 과정을 반복
- 레지스터는 CPU 내부의 초고속 임시 저장 공간 (RAM보다 100배 빠름)
- 클럭 속도만으로는 성능을 판단할 수 없으며, IPC와 아키텍처도 중요
- 파이프라이닝은 여러 명령어를 동시 처리해 성능 향상 (해저드 주의)
- 실무에서는 캐시 효율, 분기 최소화, 병렬 처리를 고려한 코드 작성 중요
💬 여러분의 경험을 공유해주세요!
CPU 구조를 이해하고 나서 코드 최적화에 성공한 경험이 있으신가요?
또는 파이프라이닝, 캐시 등에 대해 더 궁금한 점이 있다면 댓글로 남겨주세요!