오퍼랜드로 연산 코드를 수행하라 - 오퍼랜드(operand) : 명령어를 수행할 대상 : 피연산자 - 대상(데이터)이 직접 명시되기도 하고, 대상의 위치*가 명시되기도 함
오퍼랜드가 개수가 정해져있지 않음
- 오퍼랜드에 따라서 얼마나 복잡한지에 따라서 개수가 달라질 수는 있다.
- 연산 코드(op-code) 오퍼랜드로 수행할 동작
- 대표적인 명령어 연산코드는 정해져있다.
대표적인 연산코드
주소 지정
오퍼랜드(operand) : 명령어를 수행할 대상 : 대상(데이터)이 직접 명시되기도 하고, 대상의 위치*가 명시되기도 함
-> 왜 위치가 명시됌? 명령어의 길이는 한정되어 있기 때문
주소지정 학습
- 유효 주소 : 연산 코드에 사용할 데이터가 저장된 위치, 즉 연산의 대상이 되는 데이터가 저장된 위치
주소지정
- 유효 주소를 찾는 방법
- CPU 마다 차이가 있다!
1. 즉시 주소 지정
- 연산에 사용할 데이터를 오퍼랜드 필드에 직접 명시
- 가장 빠른 주소 지정 + 데이터 크기에 제한
2. 직접 주소 지정 : cpu가 레지스터에 접근하는 속도보다 메모리에 접근속도가 휠씬 더 느림.
- 오퍼랜드 필드에 유효 주소(연산에 사용될 데이터가 저장된 메모리 주소) 명시
- 오퍼랜드 필드로 표현 가능한 메모리 주소 크기에 제한
3. 간접 주소 지정
- 오퍼랜드 필드에 연산코드에 사용될 데이터를 가르키고 있는유효 주소의 주소 명시
- 유효 주소 크기에 제한은 없으나, 속도가 비교적 느림
4. 레지스터 주소 지정
- 연산에 사용할 데이터를 저장한 레지스터를 오퍼랜드 필드에 직접 명시
- 레지스터 접근은 메모리보다 빠르다!
5. 레지스터 간접 주소 지정
- 연산에 사용할 데이터를 메모리에 저장하고, 그 주소(유효 주소)를 저장한 레지스터를 오퍼랜드 필드에 명시
- 메모리 접근은 한 번 : 간접보다 메모리주소지정이 더 크다
데이터
이진수와 2의 보수
컴퓨터는 0과 1만을 이해한다 - 모든 양수를 0과 1로 표현하는 방법? : 이진수 - 모든 음수를 0과 1로 표현하는 방법? : 2의 보수 - 모든 소수를 0과 1로 표현하는 방법? : 부동 소수점 - 모든 문자를 0과 1로 표현하는 방법? : 문자 집합 & 인코딩
이진수(이진법) - 이진법: 0과 1로 모든 수를 표기하는 방법 - 이진수: 0과 1만으로 표현된 수 - 이진수로 표현할 때는, 10진수랑 헷갈릴수도 있어서 (2) 아래첨자를 사용하거나, 0b를 붙인다. - 1을 넘어가는 시점에 자리올림! - 단점 : 숫자가 너무 길어진다!
십육진법(십육진수) - 십육진법: 1~9과 A~F로 모든 수를 표기하는 방법 (A=10, B=11, C=12, D=13, E=14, F=15) - 이진수: 1~9과 A~F로 표현된 수 - 15(F)을 넘어가는 시점에 자리올림
2의 보수법 (2'd complement ) - 0과 1만으로 음수를 표현하는 방법 중 하나 - 어떤 수 n을 그보다 큰 2^n에서 뺀 값 : 11(2)의 보수 = 100(2)-11(2) = 01(2) - 모든 0과 1을 뒤집은 다음 1을 더해주면 됨 - 0과 1만으로 음수를 표현하면 양수와 음수를 구분하기 어렵지 않을까? - 그래서 사용하는 CPU 내부의 정보, 플래그(flag)! - 플래그: CPU가 명령어를 실행하는 과정에서 참고할 정보의 모음 - 음수 플래그가 세팅 되어 있을 경우: 음수 / 음수 플래그가 세팅 되어 있지 않을 경우: 양수
부동소숫점(floating point) - 소숫점이 움직일 수 있다. <> 고정소숫점 - 컴퓨터에서는 대부분 부동 소숫점이라, 부동소숫점만 이용
- 컴퓨터 내부의 소수 표현 방식(부동 소수점 표현 방식) 1.23123 (가수) X 10^2(지수) - 이진수를 m X 2^n 꼴 - 가수 부분은 1.XXX 꼴을 띄고 있음, XXX 부분(소수 부분)만 저장하면 됨
- 이와 같은 표현의 문제 : 십진수 소수를 이진수로 표현할 때 십진수 소수와 이진수 소수 표현이 딱 맞아 떨어지지 않을 수 있다 - 1/10이라는 분수 m X 10^n 꼴로 표현하면 딱 떨어진다 - 1/10이라는 분수 m X 2^n 꼴로 표현하면 무한히 많은 가수가 필요하다
문자 인코딩과 디코딩 - 문자 집합(character set) : 표현 가능한 문자들의 집합 - 문자 인코딩: 문자를 0과 1로 이루어진 문자 코드로 변환 - 문자 디코딩: 0과 1로 이루어진 문자 코드를 문자로 변환
- 아스키 문자 집합: 초창기 문자 집합, 알파벳 + 아라비아 숫자 + 일부 특수 문자 + 제어 문자
- 7bit, 2^7 : 128개의 표현으로 인코딩 할 수 있다.
- 대응이 곧 인코딩이다.
- 유니코드 문자 집합 : 대부분 언어, 특수 문자, 이모티콘, 화살표 등 모두 가능 - 특수문자, 모든 언어 가능(한국어 가능) - utf-8, utf-16 등등
ALU (산술논리연산장치) : 연산을 수행하는 장치 (계산을 담당하는 회로) - 레지스터로부터 피연산자(연산의 대상)을 받아들이고 - 제어 장치로부터 제어 신호(연산할 작업)를 받아들인다 - 연산의 결과를 레지스터, 플래그 레지스터에 저장한다 - 플래그 래지스터 : 연산의 결과에 대한 부가 정보
제어 장치: 명령어를 해석하고 제어 신호를 내보내는 장치
- 명령어 레지스터 : 해석해야할 명령어
- 클럭(Clock) 신호 : 부품을 일사분란하게 움직일 수 있게 하는 시간 단위
- 부품이 움직이는 ‘박자’ - 클럭 신호가 빠르게 반복된다면? : 빠르게 반복 - 클럭 신호가 느리게 반복된다면? : 느리게 반복
레지스터(들): 명령어 처리 전후로 값을 임시 저장하는 장치 : 가장 중요한 장치 - 프로그램의 실행 전후로 값을 임시 저장하는 작은 저장장치 - 레지스터에 어떤 값이 저장되는지만 관찰해도 프로그램의 저수준의 흐름을 볼 수 있다 - CPU마다 레지스터의 이름, 크기, 종류가 다양하다 1. 프로그램 카운터 : 메모리에서 가져올 명령어 주소 (메모리에서 읽어들일 주소) - 프로그램 카운터는 일반적으로 1씩 증가되며 메모리의 프로그램이 순차적으로 실행된다 - 일반적이지 않은(특별한) 경우 : Jump 명령어가 발생, 인터럽트가 발생한 경우 등등 : 빈번하게 발생 2. 명령어 레지스터 : 해석할 명령어 (메모리에서 읽어들인 주소) 3. 메모리 주소 레지스터 : 메모리의 주소 (읽어들일 주소 값) 4. 메모리 버퍼 레지스터 : 메모리와 주고받을 명령어와 데이터 5. 플래그 레지스터 : 연산 결과에 대한 부가 정보 저장 6. 범용 레지스터 : 범용적으로 사용 가능한 레지스터 (여러 개 있음) 7. 스택 포인터 : 스택 주소 지정 방식에서 사용되는, ‘스택의 꼭대기’를 가리키는 레지스터 - pop, stack 8. 베이스 레지스터 : 변위 주소 지정 방식에서 사용되는, ‘떨어진 거리’를 가리키는 레지스터
변위 주소 지정 방식 : 오퍼랜드 필드의 값을 변위 삼아, 특정 레지스터 값을 더해 유효 주소를 얻는 주소 지정
- 상대 주소 지정 : 오퍼랜드 + 프로그램 카운터 == 유효 주소 - 베이스 레지스터 주소지정 : 오퍼랜드 + 베이스 레지스터(기준 주소) == 유효 주소
동기 인터럽트 (= 예외, Exception) - 주로 CPU에 의해 발생 - 명령어 처리 도중 비정상적인 상황에 마주했을 경우 발생
비동기 인터럽트 (하드웨어 인터럽트) - 주로 입출력장치에 의해 발생 - 세탁기 완료 알림, 전자레인지 조리 완료 알림과 같은 ‘알림’ 역할 수행
하드웨어 인터럽트 처리 순서 1. 입출력장치는 CPU에게 인터럽트 요청 신호를 보냄 2. CPU는 실행 사이클 이후 인출 전 인터럽트 여부 확인 3. CPU는 인터럽트 요청 확인 후, 인터럽트 플래그를 통해 인터럽트 수용 여부 확인 4. 인터럽트가 가능하다면 지금까지의 작업 백업 : 메모리 내 스택 영역 5. 인터럽트 벡터를 참고하여인터럽트 서비스 루틴(인터럽트 핸들러 실행) 6. 인터럽트 서비스 루틴 실행 후 백업한 작업 복구, 실행 재개
인터럽트 요청 신호: CPU 작업을 방해하는 인터럽트에 대한 요청
인터럽트 벡터: 인터럽트 서비스 루틴의 시작 주소를 포함하는 인터럽트 서비스 루틴의 식별 정보
인터럽트 서비스 루틴: 인터럽트를 처리하는 프로그램
멀티 코어와 멀티 프로세서
클럭 - 컴퓨터 부품은 클럭 신호에 맞춰 일사 분란하게 움직인다 - CPU는 클럭 신호에 따라 명령어 사이클에 맞춰 명령어들을 실행한다 - 클럭 속도가 높은 CPU는 일반적으로 성능이 좋다 - 클럭 속도 (Hz): 1초에 반복된 클럭의 횟수로 측정 ( 1GHz = 10^9Hz)
CPU 오버클럭킹(오버(Over) + 클럭(Clock)) - 임의로 클럭 속도를 끌어올리는 기술 - 부팅시 BIOS에서 설정 가능
클럭과 발열의 관계 - 그저 무지막지하게 클럭 수를 높이면 성능이 비례하여 상승할까? NO : 클럭 수가 높아질 수록 발열 문제가 심각해지기 때문
클럭 수를 높이는 방법 이외에 성능을 높일 수 있는 방법은? 1. 코어(Core) 수 늘리기! : 멀티 코어 프로세서 - 코어: 명령어를 인출하고, 해석하고, 실행하는 CPU 내 부품 -> 코어가 여러 개 있다면? 한 번에 여러 명령어 인출, 해석, 실행 가능 2. 스레드 수를 늘리기! : 멀티 스레드 프로세서 - ‘스레드’ 용어에 대한 혼동 바로잡기 - CPU의 스레드 : 하드웨어적 스레드: 하나의 코어가 동시에 처리하는 명령어 단위 - 소프트웨어 스레드: 하나의 프로그램을 독립적으로 실행하는 단위 - 멀티 스레드 : CPU 여러 개의 하드웨어적 스레드로 한 코어로 여러 명령어를 실행 가능한 CPU - 레지스터 세트 : 하나의 명령어를 실행하기 위해 꼭 필요한 레지스터 집합
- 메모리 + 메모리에 저장된 프로그램 입장에서 하드웨어 스레드와 코어를 구분할 수 있을까? NO : 각 하드웨어를 마치 하나의 단일 스레드/코어 프로세서로 인식 - 이런 점에서 하드웨어 스레드를 논리 프로세서라고 부르기도 함
명령어 병렬 처리
CPU의 성능을 높일 수 있는 설계 : 높은 클럭 수, 멀티 코어, 멀티 프로세서
-> 이 외의 방법 : 명령어 병렬 처리 기법
명령어 파이프라이닝 (pipline+ing) : 공장의 생산라인처럼 돌아가는 것 - 공장의 생산 라인과 같이 명령어가 처리되는 서로 다른 단계를 동시에 처리하는 기법
명령어 파이프라이닝
명령어 파이프라이닝을 안 한 경우
명령어 파이프라이닝에 실패하는 시나리오 : 파이프라인 위험(pipeline hazard) 1. 데이터 위험(data hazard) : 명령어 간 의존성에 의해 발생 겹쳐 실행하기 어려운 명령어도 있다! - 명령어 간의 의존성 : 명령어 A가 저장되어야 명령어 B가 실행가능한 경우 ① 데이터가 쓰여진 직후 그 데이터를 읽어들이는 경우 (RAW, read after write) ② 데이터를 쓴 직후 그 데이터에 새 내용을 쓰는 경우(WAW, write after write ) ③ 데이터를 읽어들인 직후 그 데이터에 새 내용을 쓰는 경우 (WAR, write after read)
2. 제어 위험(control hazard) - 프로그램 카운터의 갑작스러운 변화에 의해 발생 (분기 : 분기 예측(branch prediction) : jump의 경우
3. 구조적 위험(structural hazard) - 서로 다른 명령어가 같은 자원을 사용하려 할 경우 발생 - 같은 부분 사용, 같은 메모리 사용 등등
파이프라이닝의 발전: 슈퍼스칼라 -> 최근 쓰는 파이프라이닝 - 다수의 명령어 파이프라인을 두는 방식 - 여러 명령어 동시 인출/해석/실행/저장이 가능한 CPU
- 속도가 2배 증가 X -> 늘어날수록 명령어 의존도가 늘어나기에 조금 다름
파이프라이닝을 십분 활용하기 위한 CPU 구조
- CISC : Complex Instruction Set Computer(CPU) (Intel x86 CPU) -복잡하고 다양한 기능의 명령어 제공 - 다양한 주소 지정 방식 제공 - 적은 명령어 수로 명령어 실행 가능 : 메모리를 절약을 할 수 있음 - 단점 : 하나의 명령어 실행에 일정하지 않은 클록 수 -> 명령어 파이프라이닝에 불리 - 단점 :대부분의 명령어는 사용되지 않는다! : “CISC 명령어 중 20% 정도의 명령어가 전체 명령어의 80%를 차지” : 어차피 쓰인것만 쓰인다.
- RISC : Reduced Instruction Set Computer (ARM CPU) : 최근 주목받기 시작 -짧고 규격화된 명령어 -> 명령어 파이프라이닝에 유리 -적은 수의 명령어 제공 -메모리 접근 최소화 (레지스터 활용) - 단점 : CISC에 비해 더 많은 명령어로 실행 -> 컴파일러의 역할이 중요
- RISC 같은 CISC: 마이크로 명령어 - CISC 같은 RISC: 최근 실제 RISC 명령어는 단순하지 않다
비 순차적 명령어 처리( Out-of-Order-Execution) - 파이프라이닝 내의 의존 관계가 없는 명령어를 순차적으로 처리하지 않는 방법 - 명령어를 순차적으로만 실행해서는 성능 향상에 실패하는 경우가 있음 -> 순서를 바꾸어 실행해도 프로그램 실행에 없는 명령어 순서를 바꿈 : 파이프라이닝의 성능을 높이는 기능
메인메모리와 캐시 메모리
RAM & ROM
RAM (Random Access Memory) : 임의 접근 가능 - "(메인) 메모리”라는 용어는 RAM을 지칭하는 경우가 많다 - 휘발성 저장장치 - 많은 프로그램을 동시에, 빠르게 실행하는 데에 유리 - RAM의 종류 - DRAM(Dynamic RAM): 시간이 지나면 점차 저장된 데이터가 사라지는 RAM : 메인 메모리에서 주로 사용되는 RAM - SRAM(Static RAM): 시간이 지나도 저장된 데이터가 사라지지 않는 RAM : 캐시 메모리에서 주로 사용되는 RAM -SDRAM(Synchromous Dynamic RAM) : 클럭과 동기화된 DRAM :클럭의 타이밍에 맞춰 CPU와 정보를 주고받을 수 있는 RAM -DDR SDRAM(Double Data Rate SDRAM): 대역폭을 넓혀 속도를 높인 DRAM :SDRAM (SDR;Single Data Rate RAM) - DDR2 SDRAM: DDR SDRAM보다 대역폭 두 배 넓은 RAM :DDR SDRAM보다 대역폭이 두 배 넓은 SDRAM - DDR3 SDRAM : 대역폭을 넓혀 속도를 높인 RAM :DDR2 SDRAM보다 대역폭이 두 배 넓은 SDRAM
ROM (Read Only Memory) : 읽기 전문 - 비석과도 같은 저장 장치 ; 한번 저장하면 읽기만 가능 - 냉장고, 전자제인지, 게임기, 텔레비젼과 같은 가전제품에서도 많이 사용 - ROM의 종류 - Mask ROM : 가장 기본적인 형태의 ROM : 제조 과정에서 저장할 내용을 미리 기록( (옛날) 냉장고, 전자레인지, 텔레비전, 게임기) - PROM(Programmable ROM): 데이터를 한 번 새길 수 있는 ROM : 한 번에 한해 사용자가 직접 원하는 데이터를 써넣을 수 있는 ROM (비어 있는 PROM을 얻어 안에 원하는 내용을 한 번 새기기) - EPROM (Erasable PROM): 지우고 다시 저장 가능한 PROM : 자외선 혹은 전기를 이용해 지우기 가능 - 자외선을 이용해 데이터를 지울 수 있는 ROM: UVEPROM (Ultra-Violet PROM) - 고전압의 전기 신호를 이용해 데이터를 지울 수 있는 ROM: EEPROM(Electrically Erasable PROM) - 플래시 메모리: EEPROM의 발전된, 저렴한 형태, 반도체 기반의 저장장치 : 메인 메모리 범주에 속한다기보다는 ‘범용성 넓은 저장장치’ - 사실상 대부분의 가전제품에서 활용 ( 보조기억장치로도 사용 (USB Memory, SD 카드, SSD) )
리틀 엔디안과 빅 엔디안
메모리에 데이터를 밀어넣는 순서 - 일반적으로 메모리는 바이트 단위로 저장 -하지만 CPU로부터 메모리가 받아들이는 데이터는 4 바이트(32 비트), 혹은 8 바이트 (64 비트) 워드(Word) 단위 -1 바이트씩 저장하는 메모리의 경우 : 4바이트 데이터는 네 개의 주소에 걸쳐 저장 ( 1A2B 3C4D 는 1A, 2B, 3C, 4D로 나누어 4 개의 주소에 걸쳐 저장) -1 바이트씩 저장하는 메모리의 경우 : 8바이트 데이터는 여덟 개의 주소에 걸쳐 저장 ( 1A2B 3C4D 5A6B 7C8D는 1A, 2B, 3C, 4D, 5A, 6B, 7C, 8D로 8개의 주소에 걸쳐 나누어 저장)
엔디안 - 연속해서 저장해야 하는 바이트를 저장하는 순서 - 나라마다 글을 읽는 순서가 다르듯, 메모리에 바이트를 밀어넣는 순서도 다르다 - 리틀 엔디안, 빅 엔디안
빅 엔디안 - 낮은 번지 주소부터 상위 바이트부터 저장하는 방식 - 여기서 상위 바이트란 수를 이루는 가장 큰 값(MSB) : 가장 처음 접근해야하는 글자 (e.g. 1A2B 3C4D 에서 최상위 바이트는 1A) (1A2B 3C4D 를 빅 엔디안 방식으로 저장 -> 낮은 주소번지부터 1A, 2B, 3C, 4D 순으로 저장) - 일상적으로 읽고 쓰는 숫자 체계를 읽는 순서와 동일하기 때문에 메모리 값을 직접 읽기 편리 (디버깅) - 빅 엔디안 방식으로 주소에 1A, 2D, 3C, 4D로 저장된 값은 그대로 1A2D3C4D 로 읽으면 그만
리틀 엔디안 -낮은 번지 주소부터 하위 바이트부터 저장하는 방식 -여기서 하위 바이트란 수를 이루는 가장 작은 값(LSB) ( e.g. 1A2B 3C4D 에서 최하위 바이트는 4D) ( 1A2B 3C4D 를 리틀 엔디안 방식으로 저장 -> 낮은 주소번지부터 4D ,3C ,2B, 1A 순으로 저장) - 수치 계산(자리 올림 등)이 편리
서로 다른 시스템 간에 데이터를 전송할 때에는 엔디안을 고려해야 한다
NUXI Problem: 엔디안을 고려하지 않아 ”UNIX”가 “NUXI” 꼴로 보인 문제
오늘날 데이터 송수신 간에 사용자가 엔디안을 고려하지 않는 이유 : 네트워크 전송시 엔디안이 빅 엔디안으로 통일되었기 때문
주소공간
물리 주소: 실제 메모리의 하드웨서 상의 주소
논리 주소: CPU와 실행 중인 프로그램이 사용하는 주소 (0번지부터 시작) - 모든 프로그램은 0번지부터 시작하는 각자의 논리 주소를 사용한다 - CPU는 0번지부터 시작하는 각 프로그램의 논리 주소를 인출/해석/실행
CPU/프로그램이 사용하는 주소 체계(논리 주소)와 메모리가 사용하는 주소 체계(물리 주소)가 다르다면 어떻게 문제없이 부품 간 통신이 가능할까? : CPU와 메모리 사이의 주소 변환이 이루어지면 된다! - 논리 주소와 물리 주소 간의 변환을 담당하는 장치: MMU(Memory Management Unit) - MMU의 기본 동작
- 베이스 레지스터를 활용한 주소 변환 - 베이스 레지스터 = 기준 주소 - 논리 주소 = 기준 주소로부터 떨어진 거리
한계 레지스터도 MMU에 영향을 주는 레지스터 : 한계 레지스터는 프로그램의 크기가 담김 - 그 한계를 넘으면 안 됨
저장장치 계층 구조와 캐시 메모리
저장장치 계층구조 - CPU와 멀어질수록 달라지는 저장장치의 특성 - 레지스터 vs 메인 메모리 - 메인 메모리 vs 보조기억장치 - 보조기억장치 vs 클라우드 저장 장치 저장장치 계층구조 - CPU와 멀어질수록 달라지는 저장장치의 특성 ->빠른 접근 속도와 큰 용량이 양립하기 어려운 이유 1. CPU와 가까운 저장 장치는 빠르고, 멀리 있는 저장장치는 느리다. 2. 속도가 빠른 저장 장치는 용량이 작고, 가격이 비싸다
- CPU가 아무리 빨리 정보를 처리해도 메모리가 발맞춰주지 않으면 말짱 헛수고!
그래서 등장한 캐시 메모리! - CPU와 메모리 간의 속도 차를 극복하기 위해 탄생 - CPU와 메모리 사이에 위치, 레지스터보다 용량이 크고 메모리보다 빠른 SRAM 기반 - CPU에서 사용할 법한 정보를 미리 가져와 저장 - 메모리가 대형 마트라면 캐시 메모리는 편의점
- 여러 단계의 캐시 메모리 - L1(Level 1) 캐시 메모리 CPU와 가깝고 용량 작음 - L2(Level 2) 캐시 메모리 -L3(Level 3) 캐시 메모리 CPU와 멀고 용량 큼
-분리형 캐시: L1D(데이터 저장) + L1I(명령어 저장)
캐시 메모리에 데이터가 있을 때와 없을 때 - 캐시 메모리는 CPU에서 사용할 법한 내용을 미리 가져와 저장한다 -캐시 메모리가 CPU가 요구하는 정보를 저장할 경우: 캐시 히트 (메모리 접근 X) -캐시 메모리가 CPU가 요구하는 정보를 저장할 경우 : 캐시 미스 (메모리 접근) -캐시 히트율: 캐시 히트 횟수 / (캐시 히트 횟수 + 캐시 미스 횟수)
캐시 메모리는 어떤 데이터를 저장할까? -> CPU가 자주 사용할 법한 내용 - 그렇다면 CPU가 자주 사용할 법한 내용은 어떻게 예측할까? -> 참조 지역성의 원리 (locality of reference) * 참고 지역성의 원리 : 경향성 1. 시간 지역성: CPU는 최근에 접근했던 메모리 공간에 다시 접근하려는 경향이 있다. 2. 공간 지역성: CPU는 접근한 메모리 공간 근처를 접근하려는 경향이 있다.
캐시 친화적 코드
캐시 친화적 코드: 캐시 미스가 최소화되는 코드 - 시간 지역성 / 공간 지역성을 준수하는 코드
캐시 친화적인 코드 - matrix 변수는 10,000 x 10,000 크기의 2차원 리스트로 초기화 - 중첩된 for 루프를 사용하여 매트릭스의 각 요소를 순차적으로 접근 - 각 요소 matrix[i][j]의 값을 1로 변경 - 메모리 접근 패턴이연속적이고공간적 지역성(spatial locality)을 잘 활용하고 있기 때문에 캐쉬 친화적
# matrix라는 변수 생성
# 10000x10000 크기의 2차원 리스트(또는 배열) 저장
# 이 리스트의 모든 요소를 0으로 초기화
matrix = [[0] * 10000 for _ in range(10000)]
for i in range(10000):
for j in range(10000):
matrix[i][j] = 1
캐시친화적이지 않은 코드 - matrix 변수는 10,000 x 10,000 크기의 2차원 리스트로 초기화 - for 루프에서 i가 고정된 상태에서 j를 먼저 순회하며, matrix[j][i] 값을 1로 변경 ( **열(column)**을 먼저 순회한 후 **행(row)**을 순회하는 구조) - 메모리 접근이 불연속적으로 이루어지며, 캐시 메모리가 효과적으로 작동하지 않기 때문에 캐시 미스가 빈번하게 발생
# matrix라는 변수 생성
# 10000x10000 크기의 2차원 리스트(또는 배열) 저장
# 이 리스트의 모든 요소를 0으로 초기화
matrix = [[0] * 10000 for _ in range(10000)]
for i in range(10000):
for j in range(10000):
matrix[j][i] = 1
보조기억장치와 입출력 장치 (주변 장치)
하드 디스크와 플래시 메모리
대표적인 보조기억장치 - 하드 디스크 드라이브 (하드 디스크, HDD) - 플래시 메모리 ( 솔리드 스테이트 드라이브(solid-state drive, SSD), USB 메모리, SD 카드)
하드 디스크 -LP, CD/DVD 플레이어와 유사 - 구성 요소 - 플래터: 하드 디스크 상에서 실질적으로 데이터가 저장되는 부분 - 스핀들: 플래터를 회전시키는 부분 - 헤드: 플래터의 데이터를 읽고 쓰는 부분 - 디스크 암: 헤드를 옮기는 부분 - 복수의 헤드와 플래터가 있는 경우가 많음 - 플래터의 양면을 사용하는 경우가 많음 - 하드 디스크의 데이터 단위 - 트랙: 플래터 상의 동심원 - 섹터: 트랙을 나눈 단위 (가장 작은 단위) - 실린더: 여러 개의 트랙을 모은 단위 - 블록: 실제 입출력이 수행되는 단위 : 실제로 가장 작은 단위 - 하드 디스크의 지연 시간 - 탐색 시간: 헤드를 원하는 섹터까지 이동시키는 시간 - 회전 지연: 원하는 섹터를 헤더까지 회전시키는 시간 - 전송 시간: 데이터를 송수신하는 시간
플래시 메모리 : 반도체 기반의 저장 장치 - 매우 범용성 넓은 저장 장치 : 오늘날의 가전제품에 모두 들어있다고 생각. - NAND 플래시 메모리와 NOR 플래시 메모리가 있음 - 일반적으로 사용되는 것은 NAND 플래시 메모리 - 셀(cell): 플래시 메모리의 가장 작은 저장 단위 - 한 셀에 몇 비트까지 저장이 가능한지에 따라 플래시 메모리의 종류가 나뉨 - SLC 타입: 한 셀에 한 비트 저장 가능 - MLC 타입: 한 셀에 두 비트 저장 가능 - TLC 타입: 한 셀에 세 비트 저장 가능 - QLC 타입: 한 셀에 네 비트 저장 가능 - 플래시 메모리의 저장 단위 셀 < 페이지(읽기와 쓰기 단위) < 블록(삭제단위) < 플레인 < 다이
RAID
보조기억장치를 더 안전하게, 안정적으로 저장하는 방법
데이터의 안전성, 높은 성능을 위해 여러 보조기억장치를 하나처럼 사용 가능한 기술
RAID 레벨 - RAID를 구성하는 방법 - RAID 0, RAID 1, RAID 2, RAID 3, RAID 4, RAID 5, RAID 6, RAID 10, RAID 50, ...
- RAID 0 = 스트라이핑(분산하여 저장) - 데이터를 단순히 보조기억장치에 나누어 저장하는 구성 방식: 성능 개선 / 신뢰성은 감소 - 마치 줄무늬처럼 저장된 데이터: 스트라입 - 빠른 입출력 속도, 성능 개선 가능 - 신뢰성 감소 : 1개가 고장나면 모든 게 쓸모 없어 질 수 있음
-RAID 1 = 미러링(복사본을 만드는 방식) - 쓰기 성능의 감소, 저장 공간 감소, 신뢰성 증가(복구 용이) -RAID 4 -패리티 비트(parity bit)라는 오류 검출용 비트를 저장하는 장치를 따로 두는 방식 - 단, 패리티 비트를 저장한 디스크에 병목 현상이 증가 - RAID 1에 비해 적은 하드 디스크로도 신뢰성 증가 가능 * 패리티 비트? • 홀수 패리티: 전체 1의 개수가 홀수가 되도록 패리티 비트를 정하는 방식 • 짝수 패리티: 전체 1의 개수가 짝수가 되도록 패리티 비트를 정하는 방식 • 두 개 이상의 비트에 문제가 생길 경우 오류 검출 불가능 : • 본래 패리티 비트는 오류 검출용 비트일 뿐, 복구는 불가능, RAID에서는 어느 정도의 복구가 가능
-RAID 5 -패리티 비트를 분산하여 저장하는 방식 -RAID 4의 병목을 해소하는 방식
-RAID 6 -패리티를 두 개 두는 방식 -RAID 4 혹은 5보다 더욱 신뢰성이 높아진 방식 (단, 쓰기 성능은 감소)
- nested RAID - 여러 RAID 구성 방식을 합친 방식 - RAID 10(RAID 1 + RAID 0), RAID 50 (RAID 5 + RAID 0)
입출력 기법
보조기억장치도 입출력장치의 일부로 볼 수 있다 (주변 장치) 그저 특별한 입출력장치일 뿐 보조기억장치의 입출력 과정은 타 입출력장치와 유사하다
입출력장치와 CPU는 어떻게 정보를 주고 받을까?
- 입출력 장치는 CPU 입장에서 메모리, 레지스터에 비해 다루기 까다로움 1. 매우 다양한 입출력장치의 종류 (모니터, 마이크, 키보드, 등등 모두 입출력장치임) 2. CPU와 입출력장치 간의 전송률 차
장치 컨트롤러( 입출력 제어기, 입출력 모듈) - CPU와 입출력장치 간의 통신 중개 - 오류 검출 - 데이터 버퍼링을 통한 전송률 차이 완화
장치 드라이버 - 장치 컨트롤러를 동작시키기 위한 특별한 프로그램 - 운영체제의 일부로 포함되어 있는 경우가 많음 - 입출력이란 CPU가 장치 컨트롤러와 정보를 주고받는 것 -> 그를 가능케 하는 프로그램 : 장치 드라이버
CPU와 장치 컨트롤러가 정보를 주고받는 방법 1. 프로그램 입출력 : 명령어를 기반으로 입출력을 수행하는 방법 2. 인터럽트 기반 입출력 : 인터럽트를 기반으로 입출력을 수행하는 방법 3. DMA 기반 입출력 : DMA(직접 메모리에 접근) 를 기반으로 입출력을 수행하는 방법 DMA (직접 메모리 접근) - 입출력장치와 메모리가 CPU를 거치지 않고도 상호작용할 수 있는 입출력 방식 * 입출력 작업 전달 (e.g. 메모리의 내용을 하드디스크에 백업) - 직접 메모리에 접근 가능한 입출력 기능 - 메모리로부터 백업할 정보를 읽어오고, 이를 하드 디스크의 장치 컨트롤러에 내보내기 - 입출력 작업이 끝났다면 인터럽트를 통해 작업이 끝났음을 알림 - DMA 컨트롤러가 필요 - 사이클 스틸링: CPU 입장에서는 시스템 버스를 사용할 사이클을 도둑맞음 -> 입출력 버스(PCI, PCI Express (PCIe) 버스) 를 사용하여 다른 버스를 쓸 이유가 없음