NVMe I/O Completion 모드별 성능 비교 실습
2026-1 시스템최신기술플랫폼별 QEMU 설치 및 Ubuntu VM 구축이 완료된 상태여야 합니다.
uname -r → 5.18.0-051800-generic)
preadv2(RWF_HIPRI)를 통한 동기식 Direct I/O polling을 사용합니다.
이 기능은 Linux 커널 5.19부터 제거되었기 때문에, 5.18이 이 기능을 지원하는 마지막 커널입니다.
QEMU Guest VM 안에서 커널 5.18을 사용하므로, 호스트 OS의 커널 버전과는 무관하게 polling 실습이 가능합니다.
제공된 deb 패키지로 DPAS 패치가 적용된 커널을 설치합니다.
.deb 파일은 Ubuntu/Debian 계열 Linux에서 사용하는 소프트웨어 설치 패키지입니다.
Windows의 .msi 설치 파일, macOS의 .pkg와 같은 역할입니다.
sudo dpkg -i 파일.deb 명령으로 설치하며, 커널 이미지·헤더 등 시스템 파일을 자동으로 올바른 위치에 배치해 줍니다.
uname -m을 실행하면 아키텍처를 확인할 수 있습니다.
| 호스트 환경 | uname -m 결과 | 필요한 패키지 |
|---|---|---|
| Apple Silicon Mac (M1/M2/M3/M4) | aarch64 | ARM64 |
| Intel Mac / Linux PC / Windows (WSL2) | x86_64 | x86_64 |
VM의 CPU 아키텍처에 맞는 패키지를 다운로드하세요.
다운로드한 deb 파일을 호스트에서 VM으로 전송합니다.
# 호스트 터미널에서 실행
scp -P 2222 linux-image-5.18.0-dpas_*.deb <username>@localhost:~/
scp -P 2222 linux-headers-5.18.0-dpas_*.deb <username>@localhost:~/
# VM 내부에서 실행
sudo dpkg -i ~/linux-image-5.18.0-dpas_*.deb ~/linux-headers-5.18.0-dpas_*.deb
sudo reboot
uname -r
# 5.18.0-dpas
sudo grub-reboot "Advanced options for Ubuntu>Ubuntu, with Linux 5.18.0-dpas"sudo reboot
VM 내부에서 아래 4개 스크립트를 생성합니다. 사용법: ./script.sh <device> <cpu> <numjobs>
#!/bin/bash
sudo modprobe -r nvme; sudo modprobe nvme poll_queues=0
fio --filename=/dev/$1 --size=100m --direct=1 --bs=4k --ioengine=pvsync2 \
--iodepth=1 --rw=randread --runtime=10 --numjobs=$3 --time_based \
--group_reporting --name=test --eta-newline=1 --cpus_allowed=$2 \
--nice=-10 --prioclass=2 --prio=0
#!/bin/bash
sudo modprobe -r nvme; sudo modprobe nvme poll_queues=2
fio --filename=/dev/$1 --ramp_time=3 --size=100m --direct=1 --bs=4k \
--ioengine=pvsync2 --iodepth=1 --rw=randread --runtime=10 \
--numjobs=$3 --time_based --group_reporting --name=test \
--eta-newline=1 --cpus_allowed=$2 --nice=-10 --prioclass=2 \
--prio=0 --hipri
#!/bin/bash
sudo modprobe -r nvme; sudo modprobe nvme poll_queues=2
echo 0 > /sys/block/$1/queue/io_poll_delay
echo 1 > /sys/block/$1/queue/pas_enabled
echo 1 > /sys/block/$1/queue/pas_adaptive_enabled
fio --filename=/dev/$1 --direct=1 --bs=4k --ioengine=pvsync2 \
--iodepth=1 --rw=randread --runtime=10 --numjobs=$3 --time_based \
--group_reporting --name=test --eta-newline=1 --cpus_allowed=$2 \
--nice=-10 --prioclass=2 --prio=0 --hipri
#!/bin/bash
sudo modprobe -r nvme; sudo modprobe nvme poll_queues=2
echo 0 > /sys/block/$1/queue/io_poll_delay
echo 1 > /sys/block/$1/queue/pas_enabled
echo 1 > /sys/block/$1/queue/pas_adaptive_enabled
echo 1 > /sys/block/$1/queue/switch_enabled
echo 10 > /sys/block/$1/queue/switch_param1
echo 10 > /sys/block/$1/queue/switch_param2
echo 10 > /sys/block/$1/queue/switch_param3
echo 1 > /sys/block/$1/queue/switch_param4
fio --filename=/dev/$1 --ramp_time=3 --size=100m --direct=1 --bs=4k \
--ioengine=pvsync2 --iodepth=1 --rw=randread --runtime=10 \
--numjobs=$3 --time_based --group_reporting --name=test \
--eta-newline=1 --cpus_allowed=$2 --nice=-10 --prioclass=2 \
--prio=0 --hipri
cat /sys/block/$1/queue/switch_stat
dmesg | tail -10
실행 권한 부여:
chmod +x fioint.sh fiocp.sh fiopas.sh fiodpas.sh
CPU 0에 고정하여 job 수를 변경하며 4가지 모드를 비교합니다.
# 예시: INT 모드, job 1개
./fioint.sh nvme0n1 0 1
# 각 모드별로 jobs = 1, 2, 4, 8 반복
# INT: ./fioint.sh nvme0n1 0 {1,2,4,8}
# CP: ./fiocp.sh nvme0n1 0 {1,2,4,8}
# PAS: ./fiopas.sh nvme0n1 0 {1,2,4,8}
# DPAS: ./fiodpas.sh nvme0n1 0 {1,2,4,8}
각 실행에서 fio 출력의 IOPS와 avg latency를 기록합니다.
| Mode | Jobs=1 | Jobs=2 | Jobs=4 | Jobs=8 |
|---|---|---|---|---|
| INT | ||||
| CP | ||||
| PAS | ||||
| DPAS |
DPAS 실행 후 switch_stat과 dmesg 출력에서 확인:
| MODE 값 | 의미 |
|---|---|
| 0 | INT (Interrupt) |
| 1 | CP (Continuous Polling) |
| 2 | PAS (초기 상태) |
| 3 | OL (Overloaded) |
| Jobs | 기대 최종 모드 | 이유 |
|---|---|---|
| 1 | CP (MODE 1) | QD=1 → PAS→CP 전환, polling으로 최저 latency |
| 2+ | INT (MODE 0) | QD>1 → PAS→OL→INT 전환, 인터럽트로 CPU 양보 |
switch_stat에서 pas io 수가 polled io보다 항상 적은 이유는?DPAS 커널은 /sys/block/nvme0n1/queue/ 아래에 다음 파라미터를 제공합니다.
| 파라미터 | 설명 | 기본값 |
|---|---|---|
switch_enabled | DPAS mode switching 활성화 | 0 |
switch_stat | CPU별 모드 통계 출력 (읽기 전용) | — |
pas_enabled | PAS 모드 활성화 | 0 |
pas_adaptive_enabled | Adaptive sleep 활성화 | 0 |
switch_param1 | PAS→OL 임계치 (tf > param1) | 0 |
switch_param2 | OL→PAS 임계치 (avg QD ≤ param2) | 10 |
switch_param3 | OL→INT 임계치 (avg QD > param3) | 10 |
switch_param4 | PAS→CP 전환 활성화 (0/1) | 1 |
param2=10은 average QD ≤ 1.0을 의미합니다.
이는 정수 연산으로 소수점 정밀도를 확보하기 위한 설계입니다.
deb 패키지 대신 직접 커널 소스에 DPAS 패치를 적용하고 빌드합니다.
sudo apt install -y build-essential libncurses-dev bison flex libssl-dev \
libelf-dev bc pahole dwarves zstd
cd ~
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.18.tar.xz
tar xf linux-5.18.tar.xz
cd linux-5.18
# 호스트에서 VM으로 패치 전송 후
patch -p1 < ~/dpas.patch
echo "-dpas" > localversion
cp /boot/config-$(uname -r) .config
yes '' | make localmodconfig
No space left on device)이 발생합니다.
localmodconfig는 현재 로드된 모듈만 포함하여 빌드 시간과 디스크 사용량을 대폭 줄입니다.
make -j$(nproc) bindeb-pkg
빌드 완료 시 홈 디렉토리에 linux-image-*.deb, linux-headers-*.deb 파일이 생성됩니다.
sudo dpkg -i ~/linux-image-5.18.0-dpas_*.deb ~/linux-headers-5.18.0-dpas_*.deb
sudo reboot
| 증상 | 원인 | 해결 |
|---|---|---|
No space left on device | 빌드 산출물이 디스크 초과 | make clean, tarball 삭제, localmodconfig 사용 |
Makefile Hunk #1 FAILED | 패치 버전 차이 | 무시, localversion으로 대체 |
| 부팅 후 커널 미변경 | GRUB 기본 부팅 커널 | grub-reboot으로 DPAS 커널 선택 |
| 다수 hunk 실패 | 이미 패치 적용된 소스에 재적용 | 소스를 다시 풀고 재시도 |
| 빌드 15GB+ 필요 | 디스크 여유 부족 | df -h / 확인, LVM 확장 또는 파일 정리 |
| 항목 | QEMU | 실제 하드웨어 |
|---|---|---|
| CP vs INT 비교 | 유효 (1.7-1.8x IOPS 차이) | 유효 |
| PAS adaptive sleep | 타이머 부정확, tf 노이즈 | 정확 |
| DPAS mode transition | 동작, 절대 성능은 참고용 | 기본 파라미터로 동작 |
| Multi-core 스케일링 | 에뮬레이션 오버헤드로 왜곡 | Linear에 근접 |