CPU 스케줄링·페이지 교체·I/O를 함께 튜닝하는 방법
CPU 스케줄링, 메모리 페이지 교체, I/O 스케줄러를 하드웨어와 OS 메커니즘에 맞춰 조정하는 실무 튜닝 기준
2026-08-14 · 최초 발행 2024-04-29
성능 병목은 CPU·메모리·I/O 경계를 넘는다
시스템 성능을 조정할 때 알고리즘만 따로 고르는 방식으로는 부족하다. CPU 시간을 누구에게 배분할지, 메모리가 부족할 때 어떤 페이지를 내보낼지, I/O 요청을 어떤 순서로 처리할지는 컴퓨터 아키텍처와 운영체제의 구현 방식 안에서 함께 작동한다.
CPU 스케줄링은 실행 가능한 스레드에 CPU 시간을 배정하는 정책과 메커니즘의 집합이다. FCFS, SJF/SRTF, Priority, Round Robin, MLFQ, Linux CFS 등이 여기에 속한다.
메모리 페이지 교체는 물리 메모리 한계에서 희생할 페이지를 골라 페이지 폴트 비용을 줄이는 알고리즘이다. FIFO, LRU, CLOCK(Second-Chance), Aging, ARC/LIRS가 대표적이다.
I/O 최적화는 블록 디바이스와 파일 시스템 경로에서 요청 스케줄링, 병렬 처리, 배치, 선형화를 통해 지연과 처리량을 조정하는 작업이다. NONE, mq-deadline, BFQ, Kyber, NCQ/Tagging, io_uring 기반 서브미션·컴플리션 큐를 선택지로 볼 수 있다.
하드웨어 특성이 알고리즘의 효과를 바꾼다
짧은 타임슬라이스와 잦은 컨텍스트 스위치는 L1/L2 캐시 쓰래싱을 유발할 수 있다. NUMA 환경에서는 CPU 바인딩과 메모리 바인딩을 토폴로지에 맞춰야 한다.
메모리 계층에서는 TLB 미스와 페이지 크기(THP/HugeTLB)의 상호작용이 중요하다. LRU 근사 알고리즘을 사용할 때도 kswapd, pressure와 같은 스캔 강도를 함께 조율해야 한다.
스토리지는 장치 특성을 분리해서 봐야 한다. HDD의 seek 비용과 NVMe의 병렬 큐 심도는 서로 다른 선택을 요구한다. Fsync/Barrier, writeback, merge 정책 역시 그 특성에 따라 조정 대상이 된다.
운영체제는 정책과 메커니즘을 나눠 다룬다. 정책은 어떤 작업을 우선할지 정하는 규칙이고, 메커니즘은 타임슬라이스, 런큐, 페이지 리스트, I/O 큐처럼 이를 구현하는 수단이다.
- CPU: CFS red-black tree, cgroup CPU weight/cap, rq/rd 구분
- 메모리: active/inactive LRU, page reclaim, refault detection
- I/O: blk-mq, 다중 하드웨어 큐, 스케줄러 플러그인
워크로드와 목표 지표에서 선택 기준을 잡는다
짧은 태스크가 많은지, 긴 태스크가 많은지, 혼재되어 있는지에 따라 CPU 정책의 우선순위가 달라진다. I/O도 랜덤과 순차, read-heavy와 write-heavy를 구분해야 한다.
평균 지연, tail latency(p99), 처리량, 예측 가능성 중 무엇을 우선할지 먼저 정한다. 컨테이너 집적도, 멀티테넌시 환경의 공정성, 장애가 발생했을 때 복구하기 쉬운 구성인지도 선택 조건에 포함된다.
| 항목 | 성능 | 확장성 | 일관성 | 안정성 | 운영 편의 |
|---|---|---|---|---|---|
| CFS (CPU) | 혼합 워크로드 우수 | 멀티코어/NUMA 양호 | 타임슬라이스 기반 예측성 보통 | 선점·공정성 안정 | 커널 기본값, 관리 용이 |
| MLFQ (CPU 유사 정책) | 인터랙티브 지연 우수 | 클래스 증가 시 관리 비용 | 우선순위 변동성 | 기아 방지 필요 | 파라미터 튜닝 난이도 중 |
| LRU (메모리) | 지역성을 활용 시 최상 | 큰 워킹셋에서 비용 증가 | refault 감지 시 우수 | 스캔 과도 시 위험 | 커널 근사 구현으로 편의 높음 |
| CLOCK (메모리) | 비용 대비 효과 양호 | 간단한 비트 점검로 우수 | 근사 특성으로 중간 | 고부하에서 예측성 보통 | 구현 단순 |
| mq-deadline (I/O) | 쓰기 지연 관리 우수 | 다중 큐에서 양호 | 데드라인 보장 | 스타베이션 억제 | HDD/NVMe 범용 |
| BFQ (I/O) | 대화형/동영상 등 지연 안정 | 큐 많을수록 오버헤드 | 공평성 강함 | 워크로드 전환 시 완만 | 워크로드 특화 필요 |
| NONE (I/O) | 컨트롤러 최적화에 위임 | NVMe에서 최고 처리량 | 디바이스 의존 | OS 측 지연 제어 약함 | 설정 간단 |
환경별로 달라지는 조합
초저지연 트레이딩 시스템에서는 런큐 분리와 CPU 핀닝으로 컨텍스트 스위치를 줄이고 CFS 최소 슬라이스 상향 조정을 적용할 수 있다. 프리페치와 HugePages로 TLB 미스를 낮추고 NUMA 로컬리티를 강제하며, I/O 경로는 io_uring + 직접 I/O(DIO)와 mq-deadline 또는 NONE 선택으로 구성한다.
데이터베이스 서버는 cgroup CPU weight로 백그라운드 작업을 스로틀링하고, NUMA interleave 또는 바인딩을 선택한다. 파일 캐시와 버퍼풀의 균형, vm.swappiness 하향, LRU refault 분석이 메모리 조정 대상이다. WAL/Redo 로그에는 deadline을, 대화형 쿼리에는 BFQ를 분리하거나 스토리지 계층에서 QoS를 적용할 수 있다.
컨테이너 플랫폼에서는 cgroup v2의 cpu.max와 weight로 공정성과 격리를 확보하고, MLFQ 유사 효과를 네임스페이스 레벨에서 구현한다. memory.high/oom.group로 소프트 제한과 OOM 전파를 제어하며, PSI(Pressure Stall Information) 신호를 오토스케일에 활용한다. blkio/io.weight와 스토리지 클래스별 스케줄러 분류는 noisy neighbor를 억제하는 수단이다.
이런 조정은 p99 지연 2060% 개선, CPU 캐시 미스율 1030% 감소, IOPS 1.2~3.0배 향상 가능성을 목표로 삼을 수 있다. 성능 프로파일을 일관되게 만들고, 장애 시 디그레이드 모드의 예측 가능성과 운영 자동화 난이도를 개선하는 효과도 기대할 수 있다.
측정 결과를 설계 파라미터로 되돌리는 흐름
Linux에서 확인할 CPU·메모리·I/O 설정
Linux Kernel 5.10+ 권장, systemd + cgroup v2 사용을 가정한다. NVMe SSD와 NUMA 2소켓 예시이며 root 권한이 필요하다. 배포판·커널별 디폴트 차이가 존재하므로 최신 정보 확인이 필요하다.
지연에 민감한 서비스 svcA와 백그라운드 svcB를 분리할 때는 CPU 바인딩과 cgroup 설정을 함께 적용한다.
- taskset으로 CPU 바인딩
- 예시: taskset -c 0-3 ./svcA, taskset -c 4-15 ./svcB
- cgroup v2로 공정성 설정
- 예시:
- mkdir -p /sys/fs/cgroup/svcA
- echo "max 100000" > /sys/fs/cgroup/svcA/cpu.max # 하드 캡
- echo 900 > /sys/fs/cgroup/svcA/cpu.weight # 가중치 상향
- 예시:
CFS 관련 파라미터는 다음과 같이 설정할 수 있다.
- sysctl kernel.sched_min_granularity_ns=3000000
- sysctl kernel.sched_wakeup_granularity_ns=4000000
스와핑과 캐시 스캔 강도는 다음 설정으로 제어한다.
- sysctl vm.swappiness=10
- sysctl vm.vfs_cache_pressure=50
THP와 NUMA 정책을 적용하는 예시는 다음과 같다.
- echo always > /sys/kernel/mm/transparent_hugepage/enabled
- numactl --membind=0 --cpunodebind=0 ./svcA
madvise(MADV_WILLNEED)로 프리페치하고 mlock으로 핫셋을 고정하는 방식은 C, glibc, 커널 5.x 이상 개발환경에서 사용할 수 있다.
// gcc -O2 -Wall prefetch.c -o prefetch
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
size_t sz = 256UL*1024*1024;
void* p = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
if (p==MAP_FAILED) {perror("mmap"); return 1;}
if (madvise(p, sz, MADV_WILLNEED)) perror("madvise");
if (mlock(p, sz)) perror("mlock"); // 권한 필요
// 터치하여 실제 할당 유도
for (size_t i=0;i<sz;i+=4096) ((char*)p)[i]=1;
pause();
return 0;
}
I/O 계층에서는 먼저 디바이스 스케줄러를 확인한 뒤 선택을 적용한다.
- 확인: cat /sys/block/nvme0n1/queue/scheduler
- 설정: echo mq-deadline > /sys/block/nvme0n1/queue/scheduler
큐 심도와 머지 정책은 다음 예시처럼 조정할 수 있다.
- echo 1024 > /sys/block/nvme0n1/queue/nr_requests
- echo 2 > /sys/block/nvme0n1/queue/nomerges
liburing 설치와 커널 5.10+를 전제로, io_uring 기반 경량 I/O는 다음과 같이 간단한 fio 대체 테스트로 확인할 수 있다.
- fio --ioengine=io_uring --iodepth=64 --rw=randread --bs=4k --numjobs=4 --name=t --filename=/mnt/nvme/testfile
지연 최적화가 만드는 운영상 비용
CPU weight를 높이면 다른 작업이 기아 상태에 빠질 수 있으므로 모니터링과 상한선을 함께 둬야 한다. 배리어와 저널링을 완화하면 처리량은 높아질 수 있지만, 전원 장애 때 데이터 손실 위험이 커진다.
강한 CPU 핀닝과 메모리 바인딩은 예측성을 높이는 대신 유휴 코어와 메모리를 만들 수 있다. PSI/ebpf 기반 피드백 루프를 적용하고, 실패 시 안전한 디폴트로 되돌아갈 롤백 경로를 정의하는 이유다.
CPU 스케줄링, 페이지 교체, I/O 스케줄러는 목표 지표를 기준으로 통합해 조정해야 한다. NUMA·캐시·NVMe 특성과 cgroup·blk-mq·THP 같은 OS 기능을 연결하고, 측정→조정→검증→롤백 절차 안에서 운영 파라미터를 확정한다.