1. NVMe란 무엇인가?
NVMe는 Non-Volatile Memory Express의 약자로, SSD와 같은 고속 저장장치를 효율적으로 사용하기 위해 만들어진 저장장치 인터페이스 규격이다.
NVMe는 Host Software와 SSD 사이에서
- 어떤 Command를 전달할 것인지
- Command를 어디에 저장할 것인지
- 실제 데이터가 있는 위치를 어떻게 알려줄 것인지
- 작업 완료 결과를 어떻게 전달할 것인지
등을 정의한다.
NVMe가 등장한 이유
과거 저장장치 시장에서는 HDD가 주로 사용되었다. HDD는 회전하는 Platter 위에서 Head를 움직여 데이터를 읽고 쓰기 때문에 물리적인 동작 시간이 필요하다. 따라서 저장장치 자체의 응답 속도가 비교적 느렸다.
이러한 환경에서 널리 사용된 것이 SATA Interface와 AHCI이다. AHCI는 SATA 저장장치를 제어하기 위해 만들어진 Host Controller Interface이며, 하나의 SATA Device에 대해 최대 32개의 Command Slot을 지원한다.
SATA III의 전송률은 6 Gbit/s이다. 8b/10b Encoding을 고려하면 실제 데이터에 사용할 수 있는 이론적인 전송률은 4.8Gbit/s이고, 이를 Byte 단위로 바꾸면 0.6GB/s, 즉 600MB/s 정도가 된다.
여기에 Protocol Overhead 등이 추가되므로 실제 SATA SSD의 Sequential 성능은 일반적으로 이보다 낮다.
기존 HDD 환경에 NAND Flash 기반 SSD가 등장하면서 상황이 달라졌다. SSD에는 HDD처럼 움직이는 기계 부품이 없으며, 여러 NAND Channel과 Die를 이용해 여러 작업을 병렬로 처리할 수도 있다.
저장장치 자체는 빨라졌지만 기존 SATA/AHCI 구조는 이러한 병렬 처리 능력을 충분히 활용하기 어려웠다.이를 해결하기 위해 SSD와 Non-Volatile Memory를 고려하여 설계된 것이 NVMe이다. NVMe는 많은 I/O Queue와 Command를 사용할 수 있는 구조를 제공하여 여러 I/O 요청을 효율적으로 처리할 수 있도록 설계되어 있다.
기본 용어
- Host
SSD를 사용하는 PC나 Server 측 시스템을 의미한다. CPU, Host Memory, Operating System 등이 포함된다. - NVMe Driver
Operating System의 I/O 요청을 NVMe Command로 변환하고 NVMe Controller와 통신하는 Software이다. - NVMe Controller
Host가 전달한 NVMe Command를 처리하는 SSD 측 Controller이다. 일반적인 SSD에서는 SSD Controller 내부에 NVMe Interface 기능이 구현되어 있다. - Namespace
Host가 접근할 수 있도록 NVMe Controller가 제공하는 논리적인 저장 공간이다. 정확히는 LBA(Logical Block Address)의 집합이다. Namespace 자체가 바로 Windows의 C 드라이브를 의미하는 것은 아니다. Namespace 위에 Partition과 File System이 구성되고 Operating System이 Drive Letter를 부여할 수 있다. - Command
Host가 Controller에게 어떤 작업을 수행할 것인지 전달하는 명령이다. 대표적으로 Read와 Write 등이 있다.
2. NVMe의 Queue 구조
NVMe를 이해할 때 가장 중요한 개념은 Queue이다. NVMe에서는 Host의 NVMe Driver가 Host Memory에 Queue용 공간을 준비하고, NVMe Controller와 Queue를 구성하여 Command와 처리 결과를 주고받는다.
핵심 Queue는 다음 두 가지이다.
- Submission Queue, SQ
- Completion Queue, CQ
간단히 표현하면
\[
\text{Host}
\rightarrow
\text{Submission Queue}
\rightarrow
\text{NVMe Controller}
\]
를 통해 작업을 전달하고,
\[
\text{NVMe Controller}
\rightarrow
\text{Completion Queue}
\rightarrow
\text{Host}
\]
를 통해 처리 결과를 전달한다.
2.1 Submission Queue
Submission Queue는 Host가 Controller에게 실행할 Command를 기록하는 공간이다. 예를 들어 Host가 SSD의 데이터를 읽으려 한다면 NVMe Driver는 Read Command를 만들고 이를 Submission Queue의 빈 Entry에 기록한다. Write 작업이라면 Write Command가 기록된다.
즉, Submission Queue = Controller에게 전달할 작업 목록이라고 이해하면 된다.
2.2 Completion Queue
Completion Queue는 Controller가 Command의 처리 결과를 기록하는 공간이다. Controller가 Read나 Write 작업을 완료하면 해당 Command의 처리 상태를 Completion Queue Entry에 기록한다. Host는 Completion Queue를 확인하여
- 어떤 Command가 완료되었는지
- 작업이 정상적으로 끝났는지
- Error가 발생했는지
등을 확인한다.
따라서, Submission Queue는 할 일을 전달하고, Completion Queue는 처리 결과를 전달한다라고 이해할 수 있다.
2.3 Circular Queue
Submission Queue와 Completion Queue는 Circular Queue 형태로 관리된다. Queue를 끝까지 사용하면 처음 위치로 돌아가 다시 빈 Entry를 사용하는 구조이다. Queue 내부의 현재 위치를 관리하기 위해 Head와 Tail이라는 개념이 사용된다. SQ에서는 Host가 Tail을 갱신하고 Controller가 Entry를 소비하며, CQ에서는 Controller가 Completion을 추가하고 Host가 이를 소비한다.
NVMe에서는 이러한 Queue의 위치 변화와 Doorbell Register를 이용해 Host와 Controller가 서로 현재 Queue 상태를 공유한다.
3. Admin Queue와 I/O Queue
NVMe Queue는 역할에 따라 크게 Admin Queue와 I/O Queue로 나눌 수 있다.
3.1 Admin Queue
Admin Queue는 NVMe Controller 자체의 관리와 설정에 사용된다.
예를 들어
- Controller 정보 확인
- Namespace 정보 확인
- I/O Queue 생성
- I/O Queue 삭제
- Log 정보 확인
- Firmware 관련 관리
등의 명령을 처리한다.
즉, Admin Queue = SSD를 관리하기 위한 Queue 라고 볼 수 있다.
3.2 I/O Queue
I/O Queue는 실제 사용자 데이터를 읽고 쓰기 위해 사용한다.
대표적인 Command는
- Read
- Write
등이다.
즉, I/O Queue = 실제 데이터 입출력을 처리하기 위한 Queue 라고 볼 수 있다.
시스템 초기화 과정에서 Admin Queue를 이용하여 필요한 I/O Queue를 생성한 뒤 실제 데이터 Read·Write 작업은 주로 I/O Queue를 통해 처리한다.
4. NVMe Command는 어떻게 처리되는가?
이제 실제 Read 또는 Write 요청이 어떻게 처리되는지 살펴보자. 예를 들어 Application에서 SSD에 저장된 데이터를 읽으려 한다고 가정한다.
먼저 Application의 I/O 요청이 Operating System을 거쳐 NVMe Driver에 전달된다. NVMe Driver는 요청을 NVMe 규격에 맞는 Command로 만든 뒤 Host Memory에 존재하는 Submission Queue의 빈 Entry에 기록한다.
이때 Command에는
- 어떤 작업을 수행할 것인지
- 어떤 Namespace에 접근할 것인지
- 어느 LBA부터 읽거나 쓸 것인지
- 얼마나 많은 데이터를 처리할 것인지
- 실제 데이터 Buffer가 Host Memory의 어디에 있는지
등의 정보가 포함될 수 있다.
Command를 Submission Queue에 기록한 뒤 Host는 Submission Queue Tail Doorbell Register를 갱신한다.
Doorbell은 말 그대로 Controller에게 새로운 Command가 Submission Queue에 추가되었다 는 사실을 알려주는 역할을 한다. 단순히 알림만 보내는 것이 아니라, Host가 현재 Submission Queue의 새로운 Tail 위치를 Controller에게 전달한다고 이해하는 것이 더 정확하다.
Controller는 이를 확인한 뒤 Host Memory에 있는 Submission Queue로부터 새로운 Command를 가져온다.
작업이 완료되면 Controller는 Host Memory에 있는 Completion Queue에 Completion Entry를 기록한다. 그 후 Host는 Interrupt를 받거나 Completion Queue를 Polling하여 작업 완료 여부를 확인할 수 있다. Host가 Completion Entry를 처리한 뒤에는 Completion Queue Head Doorbell을 갱신하여 해당 Entry를 소비했다는 사실을 Controller에게 알려준다.
전체 흐름을 정리하면 다음과 같다.
\[
\begin{gathered}
\text{Application} \\
\downarrow \\
\text{OS / NVMe Driver} \\
\downarrow \\
\text{Submission Queue에 Command 기록} \\
\downarrow \\
\text{SQ Tail Doorbell 갱신} \\
\downarrow \\
\text{NVMe Controller가 Command Fetch} \\
\downarrow \\
\text{Read / Write 수행} \\
\downarrow \\
\text{Completion Queue Entry 기록} \\
\downarrow \\
\text{Host가 Completion 확인} \\
\downarrow \\
\text{CQ Head Doorbell 갱신}
\end{gathered}
\]
5. NVMe Command와 실제 데이터는 어떻게 이동하는가?
예를 들어 Host가 SSD에 1 GB의 데이터를 Write한다고 하자. 그러면 1 GB의 데이터가 NVMe Command 안에 들어가서 Controller에게 전달되는 것일까?
그렇지 않다.
NVMe Command에는 실제 1 GB의 데이터가 들어가는 것이 아니라, 어떤 작업을 수행해야 하는지와 실제 데이터가 Host Memory의 어디에 있는지를 알려주는 정보 가 들어간다. 즉, Command와 실제 Data는 서로 구분된다.
개념적으로 보면 다음과 같다.
NVMe Command
├─ Read / Write 여부
├─ Namespace
├─ LBA
├─ Data Length
└─ Host Memory의 Data Buffer 위치
Host Memory
└─ 실제 Data Buffer
└─ 실제로 읽거나 쓸 데이터
5.1 Write의 경우
Application이 SSD에 데이터를 저장하려 한다고 가정한다.
실제 데이터는 먼저 Host Memory의 Buffer에 존재한다.
NVMe Write Command에는 "SSD의 이 LBA에 데이터를 저장해라." 라는 정보와 함께 "저장할 데이터는 Host Memory의 이 위치에 있다." 라는 정보가 들어간다.
NVMe Controller는 Command가 가리키는 Host Memory Buffer에 접근하여 데이터를 가져온 뒤 SSD 내부에서 처리하고 NAND Flash에 기록한다.
따라서 Write의 실제 데이터 흐름은 개념적으로
\[
\boxed{
\text{Host Memory}
\rightarrow
\text{NVMe Controller}
\rightarrow
\text{NAND Flash}
}
\]
가 된다.
5.2 Read의 경우
Read에서는 반대이다. Host는 Read Command를 통해 "SSD의 이 LBA에 있는 데이터를 읽어라." 라고 요청하면서, "읽은 데이터는 Host Memory의 이 Buffer에 저장해라." 라는 위치도 알려준다.
Controller는 NAND Flash에서 데이터를 읽은 뒤 지정된 Host Memory Buffer에 데이터를 기록한다.
따라서 Read의 실제 데이터 흐름은
\[
\boxed{
\text{NAND Flash}
\rightarrow
\text{NVMe Controller}
\rightarrow
\text{Host Memory}
}
\]
가 된다.
이러한 Data Transfer는 일반적으로 DMA(Direct Memory Access)를 이용하여 수행되므로 CPU가 데이터 하나하나를 직접 복사할 필요가 없다.
6. Controller는 Host Memory의 데이터 위치를 어떻게 알까?
여기서 한 가지 문제가 생긴다. Host Memory는 매우 크며 데이터가 반드시 하나의 연속된 Memory 영역에 존재하는 것도 아니다. Controller 입장에서는 Host Memory의 정확히 어느 주소에 데이터를 읽거나 써야 하는가?를 알아야 한다.
NVMe에서는 이를 위해 Command 안에 Data Pointer 정보를 넣는다. 대표적인 방식이
- PRP
- SGL
이다.
6.1 PRP
PRP는 Physical Region Page의 약자이다. Host Memory의 Data Buffer가 위치한 Memory Page를 Controller에게 알려주는 방식이다.
예를 들어 Write할 데이터가 Host Memory의 다음 위치에 있다고 가정하자.
Host Memory
Page A
┌───────────────┐
│ Data │
└───────────────┘
Page B
┌───────────────┐
│ Data │
└───────────────┘
Page C
┌───────────────┐
│ Data │
└───────────────┘
Controller는 이 데이터가 어느 Memory Page에 존재하는지 알아야 한다. NVMe Command의 PRP 정보가 이러한 Memory 위치를 가리킨다. 개념적으로는
NVMe Command
│
└── PRP
│
├── Memory Page A
├── Memory Page B
└── Memory Page C
와 같이 이해하면 된다.
NVMe에서는 대표적으로 PRP1과 PRP2가 사용된다. PRP1은 Host Memory에서 실제 데이터가 시작되는 주소를 가리킨다. 전송할 데이터가 추가 Page까지 이어진다면 PRP2를 이용한다.
데이터가 여러 Page에 걸쳐 길게 존재하는 경우에는 PRP2가 PRP List를 가리킬 수 있다.
NVMe Command
│
├── PRP1 ──→ 첫 번째 Data Page
│
└── PRP2 ──→ PRP List
│
├── Data Page 2
├── Data Page 3
├── Data Page 4
└── ...
따라서 PRP는 Controller가 DMA를 통해 접근해야 할 Host Memory의 Data Buffer 위치를 Page 단위로 알려주는 구조 라고 이해하면 된다.
6.2 SGL
SGL은 Scatter Gather List의 약자이다.
PRP와 목적은 비슷하다. Controller에게 실제 데이터가 Host Memory의 어디에 존재하는가? 를 알려준다. 다만 SGL은 여러 개의 Descriptor를 이용하여 Buffer의 주소와 길이를 표현한다.
예를 들어 데이터가 Host Memory 여러 위치에 흩어져 있다고 가정하자.
Host Memory
0x1000
┌──────────┐
│ Data A │
└──────────┘
다른 Memory 영역
0x5000
┌──────────┐
│ Data B │
└──────────┘
다른 Memory 영역
0x9000
┌──────────┐
│ Data C │
└──────────┘
SGL은 이를 다음과 같이 표현할 수 있다.
SGL
Descriptor 1 → Data A의 주소 + 길이
Descriptor 2 → Data B의 주소 + 길이
Descriptor 3 → Data C의 주소 + 길이
즉, 흩어져 있는 여러 Memory 영역을 하나의 데이터 전송 대상으로 표현할 수 있다.
따라서
NVMe Command
│
│ PRP / SGL
▼
Host Memory Data Buffer
│
│ DMA
▼
NVMe Controller
라는 관계를 이해하는 것이 중요하다.
7. Queue Depth란 무엇인가?
NVMe의 Queue 구조를 이해했다면 SSD Specification에서 자주 등장하는 Queue Depth(QD)도 이해할 수 있다. Queue Depth는 일반적으로 특정 시점에 완료되지 않고 대기 또는 처리 중인 I/O Request의 수를 의미한다. 예를 들어 QD1은 한 번에 하나의 I/O Request가 outstanding 상태인 경우이고, QD32는 최대 32개의 I/O Request가 outstanding 상태인 Workload를 의미한다.
여기서 주의할 점이 있다. QD32는 Queue가 32개라는 뜻이 아니다. 하나의 Queue 안에서도 여러 Command가 동시에 outstanding 상태로 존재할 수 있다.
Submission Queue
Command 1
Command 2
Command 3
...
Command 32
와 같은 상태를 생각하면 된다.
Queue Depth가 커지면 SSD Controller가 여러 I/O를 동시에 처리할 기회가 많아진다.
이를 통해
- 여러 NAND Channel
- 여러 NAND Die
- Controller 내부의 병렬 처리 자원
을 효율적으로 사용할 수 있다.
따라서 QD가 증가하면 IOPS와 Throughput이 증가할 가능성이 있다.
하지만 많은 요청이 동시에 대기하면 각 요청이 기다리는 시간이 길어질 수 있으므로 Latency 역시 증가할 수 있다.
즉,
\[
\text{높은 QD}
\not\Rightarrow
\text{무조건 좋은 성능}
\]
이다. 성능은 Workload에 따라 판단해야 한다.
8. SSD 성능은 어떻게 읽어야 하는가?
SSD 성능을 볼 때는 IOPS, Throughput, Latency를 구분해야 한다.
8.1 IOPS
IOPS는 Input/Output Operations Per Second의 약자로 초당 몇 번의 I/O 작업을 처리할 수 있는지를 나타낸다.
\[
\frac{\text{I/O Operations}}{\text{Second}}
\]
작은 데이터를 여러 위치에서 처리하는 Random I/O 성능을 표현할 때 자주 사용된다.
SSD Specification에서 흔히 볼 수 있는 예가 "4 KB Random Read"이다.
4 KB가 사용되는 이유를 단순히 "Operating System이 항상 4 KB 단위로 데이터를 저장하기 때문"이라고 이해하면 정확하지 않다. 4 KB는 Memory Page나 File System Block 등에 널리 사용되는 작은 데이터 크기이며, SSD의 작은 Random I/O 처리 성능을 비교하기 위한 대표적인 Benchmark 크기로 사용된다.
8.2 QD32 Random Read의 의미
예를 들어 4 KB Random Read, QD32라는 조건이 있다고 하자.
이는
- 약 4 KB 크기의 I/O를
- 서로 연속되지 않은 LBA에 대해 Read하고
- 최대 32개의 I/O 요청을 outstanding 상태로 유지하면서
측정한 성능이라는 의미이다.
여기서 'QD32를 32개의 Queue를 사용한다' 라고 해석하면 안 된다.
8.3 Throughput
Throughput은 초당 얼마나 많은 데이터를 전송하는가를 나타낸다.
일반적으로
\[
\mathrm{MB/s},\ \mathrm{GB/s}
\]
등으로 표현한다.
IOPS와 Throughput은 서로 다른 개념이지만 Block Size를 알면 둘 사이의 관계를 계산할 수 있다.
예를 들어 4 KB I/O를 초당 1,000,000번 처리한다고 가정한다.먼저
\[
4096\,\mathrm{Byte}
\]
이므로
\[
4{,}096{,}000{,}000\,\mathrm{Byte/s}
\]
이다.
이를 10진수 GB로 변환하면
\[
4.096\,\mathrm{GB/s}
\]
이다.
따라서
\[
\boxed{4.096\,\mathrm{GB/s}}
\]
가 된다.
즉,
\[
\boxed{
\text{Throughput}
\approx
\text{IOPS}
\times
\text{I/O Size}
}
\]
의 관계를 생각할 수 있다.
다만 실제 SSD 성능은 Protocol Overhead, Controller, NAND, Queue Depth 및 Workload 등의 영향을 받기 때문에 단순 계산값과 정확히 일치하지 않을 수 있다.
8.4 Latency
Latency는 하나의 I/O Request가 시작된 뒤 완료되기까지 걸리는 시간이다.
일반적으로
\[
\mu\mathrm{s},\ \mathrm{ms}
\]
등으로 표현한다.
IOPS가 높다고 해서 모든 I/O의 Latency가 낮다는 의미는 아니다. 높은 Queue Depth에서 많은 I/O를 병렬로 처리하면 전체 IOPS는 높아질 수 있지만, 개별 Request가 Queue에서 기다리는 시간이 증가할 수도 있다.
따라서 SSD 성능을 볼 때는
\[
\boxed{
\text{IOPS}
+
\text{Throughput}
+
\text{Latency}
+
\text{Queue Depth}
}
\]
를 함께 보는 것이 좋다.
8.5 Sequential I/O와 Random I/O
SSD Benchmark에서는 Sequential과 Random이라는 표현도 자주 등장한다.
8.5.1 Sequential I/O
Sequential I/O는 연속된 LBA 영역을 순서대로 읽거나 쓰는 작업이다. 예를 들어 대용량 File을 연속적으로 읽는 상황이 이에 가깝다. 주로
- 대용량 File Copy
- Video File
- 대용량 연속 데이터
등과 관련된다.
Sequential 성능은 보통
\[
\mathrm{MB/s},\ \mathrm{GB/s}
\]
와 같은 Throughput으로 표현한다.
8.5.2 Random I/O
Random I/O는 서로 떨어진 LBA에 위치한 데이터를 읽거나 쓰는 작업이다.
주로
- Operating System
- Application
- Database
- Server Workload
등에서 많이 발생한다.
작은 Random I/O에서는
- 4 KB Random Read
- 4 KB Random Write
와 같이 표현하고 성능 지표로 IOPS를 많이 사용한다.
9. NVMe 전체 동작 흐름
지금까지의 내용을 하나의 흐름으로 연결해 보자.
Command의 이동은 다음과 같이 볼 수 있다.
Application
↓
Operating System
↓
NVMe Driver
↓
Submission Queue
↓
NVMe/SSD Controller
↓
FTL
↓
NAND Flash
작업이 끝난 뒤 Completion 정보는 반대 방향으로 전달된다.
NAND 작업 완료
↓
NVMe Controller
↓
Completion Queue
↓
NVMe Driver
↓
Operating System
↓
Application
하지만 Command와 실제 Data의 이동 경로는 구분해서 생각해야 한다.
Write에서는
NVMe Command
↓
Submission Queue
↓
NVMe Controller
Host Memory Data Buffer
↓
DMA
↓
NVMe Controller
↓
NAND Flash
가 되고,
Read에서는
NAND Flash
↓
NVMe Controller
↓
DMA
↓
Host Memory Data Buffer
가 된다.
이때 NVMe Controller가 Host Memory의 어느 Buffer에 접근해야 하는지를 알려주는 것이 PRP 또는 SGL이다.
따라서 NVMe의 전체 구조를 이해할 때 가장 중요한 관계는 다음과 같이 정리할 수 있다.
\[
\boxed{
\text{Queue}
\rightarrow
\text{Command 전달}
}
\]
\[
\boxed{
\text{PRP / SGL}
\rightarrow
\text{Data Buffer 위치 지정}
}
\]
\[
\boxed{
\text{DMA}
\rightarrow
\text{실제 Data 전송}
}
\]
\[
\boxed{
\text{Completion Queue}
\rightarrow
\text{Command 처리 결과 전달}
}
\]
즉, NVMe는 단순히 데이터를 빠르게 보내는 기술이라기보다 Host와 SSD 사이에서 많은 I/O Command와 Data Transfer를 효율적으로 관리하기 위한 구조라고 이해하면 된다.
'Semiconductor' 카테고리의 다른 글
| DRAM I/O (0) | 2026.10.08 |
|---|---|
| NAND I/O (0) | 2026.10.07 |
| PCIe (0) | 2026.09.29 |
| Custom HBM이란? Core Die와 Base Die부터 이해하기 (0) | 2026.09.27 |
| SSD Form Factor (0) | 2026.09.24 |
