본문 바로가기
공부(TIL)/스터디

[컴퓨터 구조] 명령어

by 하루지오 2024. 4. 5.

1. 프로그래밍 언어

고급 언어

- 개발자가 이해하기 쉽게 만든 언어
- 종류
1) 컴파일 언어: 코드를 컴파일러를 통해 기계어로 변환한 후 실행하는 언어
    ex) C, C++, Java
    → 컴파일 중 오류가 발생하면 소스 코드 전체 실행 안됨

컴파일: 컴파일 언어로 작성된 소스 코드가 컴파일러의해 저급 언어(어셈블리 언어)로 변환되는 과정
1) 전처리 과정 : 외부 소스코드 및 라이브러리를 포함하고 매크로를 변환하며 컴파일할 영역을 명시
2) 컴파일 과정: 전처리가 완료된 소스 코드를 어셈블리 언어로 변환
3) 어셈블 과정: 어셈블리어를 기계어로 변환, 이 과정에서 목적 코드를 포함하는 목적 파일이 됨
4) 링킹: 목적 파일이 실행 파일으로 변환

 
2) 인터프리터 언어: 코드를 실행하기 위해 인터프리터라는 프로그램에 의해 한 줄씩 해석되고 실행되는 언어
    ex) Python, JavaScript
    → 별도의 컴파일 단계가 필요하지 않음
    소스 코드 전체가 저급 언어로 변환되기까지 기다릴 필요 없음
    → 인터프리팅 중 오류가 발생하면 오류 발생 전까지 코드 실행
 

저급언어

- 컴퓨터가 이해하고 실행하는 언어
- 종류
1) 기계어 : 0과 1로 이뤄진 명령어로 구성된 언어
2) 어셈블리어: 기계어를 읽기 편하게 번역한 언어
3) 목적코드: 컴파일 결과로 생성되는 언어
 

2. 명령어

구조

- 오퍼랜드 + 연산 코드
1) 오퍼랜드(Operand): 명령어가 작동하는데 필요한 데이터나 연산 대상,  데이터나 연산 대상저장된 위치
2) 연산 코드(Opcode): 수행할 연산으로, 명령어의 종류를 나타냄
    →  컴퓨터가 해당 명령어를 어떻게 실행할지 결정하는 데 사용
 

대표적인 연산 코드

1) 데이터 전송
ex) MOVE, STORE, LOAD, PUSH, POP
2) 산술/논리 연산
ex) ADD, SUBTRACT, MUTIPLY, DIVIDE, INCREMENT, DECREMENT, AND, OR, NOT:AND, OR, NOT, COMPARE
3) 제어 흐름 변경
ex) JUMP, CONDITIONAL JUMP, HALT, CALL, RETURN 
4) 입출력 제어
ex) READ(INPUT), WRITE(OUTPUT), START IO, TEST IO

유효 주소(effective address)

- 연산에 사용할 데이터가 저장된 위치
 

명령어 주소 지정 방식(addressing modes)

- 유효 주소를 찾는 방법
- 종류
1) 즉시 주소 지정 방식

  • 연산에 사용할 데이터를 오퍼랜드 필드에 직접 명시
  • 가장 간단한 형태의 주소 지정 방식
  • 연산에 사용할 데이터의 크기가 작아질 수 있음
  • 속도 빠름

2) 직접 주소 지정 방식

  • 오퍼랜드 필드에 유효 주소를 직접적으로 명시
  • 유효 주소를 표현할 수 있는 크기가 연산 코드만큼 줄어듦

3) 간접 주소 지정 방식

  • 오퍼랜드 필드에 유효 주소의 주소를 명시(간접적으로 명시)
  • 앞선 주소 지정 방식들에 비해 속도가 느림

4) 레지스터 주소 지정 방식

  • 연산에 사용할 데이터가 저장된 레지스터를 명시
  • 메모리에 접근하는 속도보다 레지스터에 접근하는 것이 빠름
  • 연산에 사용할 데이터를 메모리에 저장
  • 그 주소를 저장한 레지스터를 오퍼랜드 필드에 명시
  • ex) 스택 스택 포인터 주소 지정 방식, 변위 주소 지정 방식(오퍼랜드 필드 값+프로그램 카운터/베이스 레지스터)
변위 주소 지정 방식
1) 상대 주소 지정 방식: 오퍼랜드 필드 값+프로그램 카운터 저장 값→ 유효 주소 얻기
2) 베이스 레지스터 주소 지정 방식: 오퍼랜드 필드 값+베이스 레지스터 저장값→ 유효 주소 얻기

 

명령어 사이클

▶️ 프로그램 속 명령어들은 일정한 주기로 인출↔실행을 반복 실행
▶️ CPU는 명령어 사이클에 맞춰 명령어들을 실행
 
⬅️ 인출 사이클 : 메모리 → CPU
➡️ 실행 사이클: CPU
↩️ 간접 사이클:  메모리 → 간접  CPU ex)  간접 주소 지정 방식(메모리 접근이 더 필요한 경우)
↪️ 인터럽트 사이클 : CPU   메모리(인터럽트 서비스 루틴 처리)   메모리

명령어 처리 단계

1️⃣ 명령어 인출(Instruction Fetch)
2️⃣ 명령어 해석(Instruction Decode)
3️⃣ 명령어 실행(Execute Instruction)
4️⃣ 결과 저장 (Write Back)

명령어 병렬처리 기법

▶️ 명령어를 동시에 처리해 CPU를 쉬지 않고 작동시키는 방법
ex) 명령어 파이프라인, 슈퍼스칼라, 비순차적 명령어 처리 등

1️⃣ 명령어 파이프라인(Instruction Pipeline)
▶️ CPU는 다른 단계에 있는 여러 명령어는 동시 실행 ⭕️ 

→ 훨씬 효율적(명령어는 같은 처리 단계는 동시 실행 ❌️
▶️ 명령어가 최적화 되어야 함(=파이프라이닝을 하기 쉬운 형태로 된 설정) ex) RISC

⚠️ 파이프라인 위험(Pipeline hazard)
1️⃣ 데이터 위험(data hazard)
- 명령어 간 데이터 의존성에 의해 파이프라인이 제대로 동작하지 않는 경우
- 어떤 명령어가 이전 명령어가 끝까지 실행되기 전까지는 실행하기 어려운 경우

2️⃣ 제어 위험(control hazard)
프로그램 카운터의 갑작스러운 변화로 명령어 파이프라인이 미리 처리 중이던 명령어들은 쓸모 없게 되버린 경우
ex) 분기 예측(branch prediction): 제어 위험을 방지하기 위해 사용하며, 프로그램이 어디로 분기할지 예측하고 그 주소를 인출하는 기술

3️⃣ 구조적 위험(structural hazard, resource hazard)
명령어 파이프라인에서 서로 다른 명령어가 동시에 ALU, 레지스터 등의 CPU 부품에 접근하는 경우


2️⃣ 슈퍼 스칼라
▶️ CPU 내부에 여러 개의 명령어 파이프라인을 포함한 구조

슈퍼스칼라 프로세서(슈퍼스칼라 CPU)
▶️ 슈퍼스칼라 구조의 명령어 처리가 가능한 CPU
▶️ 이론적으로 파이프라인 개수과 프로그램 처리 속도가 비례함
▶️ 예상치 못한 상황(파이프라인 위험)에서는 파이프라인 개수에 비례해 빨라지지 ❌️

=> 파이프라인 위험을 방지하는 방향으로 고도화한 설계가 필요함.


3️⃣ 비순차적 명령어 처리(Out-of-order execution, OoOE)
▶️ 명령어를 순차적으로 실행하지 않는 기법
▶️ CPU 성능 향상에 크게 기여
▶️ 오늘날 대부분의 CPU가 차용함
ex) 1순위 명령어들과 데이터 의존성이 있을 때 나머지 순위 명령어들을 먼저 처리

※ 비순차적 명령어 처리가 가능한 CPU는 명령어가 어떤 명령어들과 데이터 의존성이 있는지, 순서를 바꿔서 실행할 수 있는지를 판단할 수 있어야 함

명령어 집합 구조(Instruction Set Architecture, ISA)

▶️ 명령어 집합(Instruction Set)
▶️ CPU가 이해할 수 있는 명령어들의 모음
▶️ CPU 하드웨어 설계 방식에 큰 영향 ⭕️
▶️ 명령어 병렬 처리에 적절한 ISA vs 적절하지 못한 ISA
▶️ CPU의 명령어 이해 방식에 따라 컴퓨터 구조 및 설계 방식이 달라질 수 ⭕️
=> CPU마다 ISA가 다를 수 있음 ex> 인텔 CPU 컴퓨터와 맥은 서로의 명령어 호환 ❌️
=> CPU마다 ISA가 다르면, 아래가 달라짐
1️⃣ 제어장치가 명령어를 해석하는 방식
2️⃣ 사용하는 레지스터 종류와 수
3️⃣ 메모리 관리 방법

CISC vs RISC

1️⃣ CISC(Complex Instruction Set Computer)
⭕️ 복잡하고, 다양한 형태와 크기의 명령어
⭕️ 가변 길이 명령어 활용
⭕️ 다양한 주소 지정 방식
⭕️ 명령어 사용 적음(메모리 효율적)
⭕️ 자주 사용되는 명령어들만 쓰임(사용빈도 적음)
❌️ 명령어 규격화❌️(비효율적인 파이프라인)
→  명령어 크기≠, 명령어 실행 시간이 일정하지 않음
→  여러 클럭에 걸쳐서 명령어 실행

2️⃣ RISC(Reduced Instruction Set Computer)
⭕️ 단순하고 적은 수의 명령어 ex) 메모리 직접 접근 명령어 load, store)
⭕️ 고정 길이 명령어 활용
⭕️ 주소 지정 방식 적음
⭕️ 명령어 사용 많음(메모리 비효율적)
⭕️ 명령어  규격화(효율적인 명령어 파이프라인) -> 1클럭 내외로 실행되는 명령어 지향
❌️ 레지스터를 적극적으로 활용 -> 레지스터를 이용하는 연산이 많고, 일반적으로 범용 레지스터의 수 많음