FDP SSD for LMCache — 배치 전략 PoC
[!tldr] 업무 관점 takeaway LMCache KV 캐시를 어떤 기준으로 FDP 스트림에 나눌 것인가? 세 가지 전략 — prompt별, prefill/decode 단계별, TP 워커별. 핵심 도전은 "수명이 다른 데이터를 어떻게 구별하느냐"이다. 구현 경로는 Raw Block + io_uring_cmd (
dspec필드)가 명시적 배치를 위한 정석. 2026-06-15 실측 + 반복실험 재해석 (2026-06-19): R0 precondition 후 3모드 측정.separated가 host/media write 최소(-27%)지만, replay operation 수가 세 모드 동일(finish_write 105,728) → host write 감소는 FDP 확정 효과가 아닌 SMART counter artifact. WAF ratio 세 모드 ~1.016으로 동일 → "FDP가 WAF를 낮췄다" 주장 불가. Phase 1: NS/span 축소로 드라이브 steady state 만든 후 재실험 필요.
목표
LMCache 스토리지에서 FDP SSD를 활용해 수명(lifetime)이 다른 KV 캐시 스트림을 분리:
- WAF(Write Amplification Factor) 감소 → SSD 내구성 향상
- 멀티테넌트 환경에서 예측 가능한 I/O 성능 (GC 간섭 감소)
LMCache의 I/O 특성
| 특성 | 설명 |
|---|---|
| 대용량 I/O | KV 청크 수 MB ~ 수십 MB |
| 덮어쓰기 없음 | WORM(Write Once Read Many) |
| 순차 접근 | 자기회귀적 특성 → 앞 토큰부터 순서대로 prefix KV 읽기 |
구현 인터페이스 선택지
| 방식 | 장점 | 단점 |
|---|---|---|
| 파일 시스템 (write hints) | LMCache 변경 최소화 | FS가 스트림 혼합 가능; NVMe 스트림 패싱 미반영 |
| Raw Block (io_uring_cmd) | 명시적 배치; NVMe passthru 고성능 | 커스텀 스토리지 엔진 필요; NVMe 전문 지식 요구 |
→ io_uring_cmd가 정석. NvmeUringCmd.cdw13 (dspec 필드)에 placement handle 직접 embed.
배치 전략 3가지
1. Prompt별 배치 (Per-Prompt)
동기: KV 청크는 rolling hash 기반 prefix hash chain을 형성 → 같은 체인의 청크는 함께 hot/cold 전환.
stream = prompt_id % num_placement_handles
제약:
num_placement_handles최대 128개 (FDP SSD 하드웨어 한계)- 다른 prompt의 청크가 같은 그룹에 매핑될 수 있음
2. 단계 인식 배치 (Phase-aware)
동기:
- decode KV: 현재 요청 처리 중에만 사용 → 단명(short-lived)
- prefill KV: 여러 요청에 걸쳐 재사용 가능 → 장명(long-lived)
prefill KV → 스트림 N (장명 스트림)
decode KV → 전용 또는 기본 스트림 (단명 스트림)
수명이 다른 두 유형을 분리하면 GC가 단명 스트림만 효율적으로 회수 가능.
3. TP 워커별 배치 (Per-vLLM Worker)
동기: TP > 1 환경에서 전체 스텝 지연 = max(rank_i I/O latency). FDP로 랭크 간 성능 격리 시 tail latency 감소.
| 워커 | Placement Group |
|---|---|
| Worker 0 | Group 0 |
| Worker 1 | Group 1 |
| Worker 2 | Group 2 |
| Worker 3 | Group 3 |
평가 시 주의사항
- 클린 상태에서 시작하면 WAF 관찰 어려움
- sustained state로 preconditioning 후 측정해야 의미 있는 WAF 수치 확보 가능
- 비교군: CacheLib FDP 사례 (WAF 3.5 → ~1.0) 참고
실측 결과 (2026-06-15, Stage 4)
셋업
- R0 window-local precondition (32GiB sequential fill + random overwrite 1회) 후 3모드 실행
- Workers 9, warmup 2회, measurement 8회, device
/dev/ng0n1
WAF + Host Write 결과
| Mode | Host write delta | Media write delta | WAF | write p99 |
|---|---|---|---|---|
no_fdp | 476 GiB | 484 GiB | 1.01587 | 1.353 ms |
mixed | 466 GiB | 474 GiB | 1.01587 | 1.290 ms |
separated | 348 GiB | 354 GiB | 1.01588 | 3.357 ms |
해석
separated는 host/media write volume이 가장 낮음 (no_fdp 대비 −27%), FDP lifetime 분리 효과 관측.- WAF ratio 자체는 세 모드 모두 약 1.016으로 거의 동일 — 이번 batch만으로 "separated가 WAF ratio를 낮췄다"는 결론은 약함.
separatedwrite p99가 3.4ms로 가장 높음 → host write는 줄었지만 write tail latency 악화 → 추가 분석 필요.- 1차 실험(precondition 없음)에서는
mixed가 host write 최저였으나, R0 precondition 후에는separated가 최저로 역전 → precondition이 결과에 크게 영향함을 확인.
다음 단계 (단일 batch 기준)
- WAF ratio 차별화를 위해 더 많은 반복 실험 + 다른 precondition 조건 필요
- write p99 악화 원인 분석 (FDP placement 오버헤드 vs. GC 패턴 변화)
- 2차 tensormesh 통합 실험으로 TTFT·hit ratio 측정
반복실험 재해석 (2026-06-19, 72 runs)
핵심 발견
72 measurement run 반복실험에서 세 모드의 replay operation 수가 완전히 동일했다:
| 항목 | no_fdp / mixed / separated 공통 |
|---|---|
| measurement run 수 | 72 |
| finish_write count | 105,728 |
| reserve_write count | 105,728 |
| prefetch count | 6,464 |
| errors | 0 |
→ separated가 store operation을 덜 수행해서 host write가 적어진 것이 아님.
host write 감소가 FDP 효과가 아닌 이유
host_write_bytes_delta=nvme smart-log Data_Units_Written차이 (애플리케이션 logical write 합산 아님).- mode별 write path는 거의 같음: no_fdp/mixed/separated 모두
use_uring_cmd=true, FDP는 placement directive(dspec)만 다름 → host write 자체를 줄이는 메커니즘이 없음. - op 수가 같은데 단일 batch에서
no_fdp 476GiB / separated 348GiB(~127GiB 벌어짐)는 R0 잔여·counter capture window·controller deferred write가 delta에 섞인 측정 artifact. - WAF ratio가 세 모드 ~1.016으로 동일 — 실제 GC copy가 줄었다면
separatedWAF가 떨어졌어야 함.
결론
"반복실험 평균에서
separated의 host/media write delta가 낮게 관측되었지만, replay operation 수는 세 모드가 동일하고 trial별 방향도 완전히 일관되지는 않았다. host write 감소는 FDP placement로 인한 확정적 애플리케이션 write 감소라기보다 현재 측정 조건에서 관측된 controller counter 차이로 해석해야 한다."
Phase 0 — PoC 소스 검증 결과 (2026-06-19)
결론: harness·generator 수정 불필요, Phase 1이 해법
- warmup persist: warmup→measurement 사이 내부 discard/reset 없음 → warmup write가 measurement까지 persist. 큰 warmup을 placement-consistent preconditioner로 쓰는 전략 성립.
- WAF window:
host_delta = after − after_warmup구간만 카운트 → warmup/R0는 WAF window 바깥. - device churn: logical 키는 write-once지만,
capacity_bytesslot-reuse로 device 물리 overwrite는 의도 설계. stress에서 ~192GiB를 1GiB window에 errors=0 → churn 충분. - WAF≈1의 진짜 원인: 9 worker × ~1GiB = ~8GiB window를 2TiB offset에 몰아 써도, 멀티-TB 드라이브는 다른 NAND가 비어 있어 GC가 valid copy를 거의 안 함 → WAF≈1.
Phase 1 방향
NS/span 축소로 usable capacity ≈ (harness 총 window span + 적정 OP)가 되게 만들어, measurement window의 overwrite가 valid-data GC를 유발하도록 → 드라이브 steady state에서 재실험.
게이트: precondition 후 no_fdp WAF > 1 확인 → FDP 효과 측정 가능 상태.
관련 페이지
- [[raw_block-io_uring-cmd]] — io_uring_cmd + dspec 필드 (FDP 배관)
- [[NVMe-FDP]] — FDP RUH/RG/PH 개념
- [[Plugin-Pipeline]] — LMCache 코드 수정 없이 FDP 백엔드 등록하는 방법
- [[CacheLib-FDP-사례]] — 외부 증거
- [[WAF]] — 개선 목표 KPI