본문으로 건너뛰기

LMCache Local Disk Backend

[!tldr] 업무 관점 takeaway 우리 미션의 정확한 코드 위치. "청크 하나 = .pt 파일 하나"로 NVMe에 떨어진다 → small/medium file이 폭주 → [[WAF]] 증가 주범. PUT은 4-worker thread pool로 async를 흉내내는 sync I/O다 (진짜 async 아님). use_odirect=True 옵션은 있지만 alignment 보장이 코드 레벨에서 검증되지 않았다. 여기를 io_uring + FDP-aware로 갈아엎는 게 [[기여-포인트-맵|기여 포인트 [1][2][8][9]]].


1. 무엇을 하는가

CPU DRAM에서 evict된 KV Cache 청크를 로컬 NVMe에 영속적으로 저장하는 [[LMCache-아키텍처|티어 3]]. CPU RAM과 Local Storage가 같은 머신에 있을 때의 오프로드 경로.


2. 설정 (핵심 옵션)

LMCACHE_CHUNK_SIZE=256 # KV Cache 청크 단위 (토큰 수)
LMCACHE_LOCAL_DISK="file:///mnt/nvme0/kvcache" # 디스크 경로
LMCACHE_MAX_LOCAL_DISK_SIZE=5.0 # 최대 사용 용량 (GB)
LMCACHE_EXTRA_CONFIG='{"use_odirect": True}' # Page Cache 우회

[!important] use_odirect가 우리 실험에 결정적 [[O_DIRECT]]를 켜야 Page Cache를 거치지 않고 NVMe에 직접 I/O가 나간다. 이렇게 해야 FDP 효과가 실제 SSD에 그대로 보인다. 단, alignment 조건 깨지면 silently fallback → 코드 레벨 검증이 [[기여-포인트-맵|기여 포인트 [5]]]에 해당.


3. Multi-Path (per-GPU NVMe 분리)

cuda:0 → /mnt/nvme0/kvcache/
cuda:1 → /mnt/nvme1/kvcache/

local_disk_path_sharding="by_gpu" 옵션. device_id % num_paths로 매핑. 멀티 GPU FDP 검증 시 변수 통제용으로 유용: GPU별 I/O 격리하면 FDP 스트림 분석이 깔끔.


4. 동작 방식 — "청크 하나 = 파일 하나" (⚠️ 핵심)

CPU RAM 오프로딩과 달리 공간 미선점 X. 청크 저장 시마다 .pt 파일 1개 생성. 용량 초과 시 LRU evict (파일 삭제).

PUT (write): 청크 1개 → .pt 파일 1개 생성
GET (read): .pt 파일 1개 → CPU 버퍼로 읽음
EVICT: .pt 파일 1개 삭제

[!warning] FDP 설계 직접 영향 수천 개의 medium file이 NVMe에서 끊임없이 생기고 지워짐 → 서로 다른 lifetime이 같은 NAND block에 섞임 → [[Garbage-Collection|GC]] 폭주 → [[WAF]] 증가. FDP RUH로 hot/cold 파일을 다른 RU에 배치하면 이 패턴이 직접 완화된다. ([[NVMe-FDP]] 참조)

PUT/GET/Prefetch 의미

오퍼레이션방식함의
put() (write)비동기추론 블로킹 없음, burst write 가능
get() (read)블로킹읽을 땐 기다림 → [[TTFT-ITL|TTFT]]에 직결
prefetch()비동기디스크 → CPU RAM (디스크 데이터는 유지)

[!note] read가 blocking이 TTFT에 직결 NVMe read latency가 곧 warm TTFT다. FDP가 GC 부담을 줄여 read p99 안정화 → TTFT 직접 개선.


5. 내부 아키텍처 — 4 컴포넌트

LocalDiskBackend
├── Metadata Dictionary
│ CacheEngineKey → (file path, size, shape, dtype, pin)
├── Cache Policy
│ LRU / LFU / FIFO / MRU
├── LocalDiskWorker ← ⚠️ 주목
│ 4-worker thread pool, priority queue
└── Local Disk
.pt 파일 저장소

Worker priority queue (흥미로운 설계 철학)

Priority작업
0 (최우선)PREFETCH
1DELETE
2 (최하위)PUT

[!note] 의외의 통찰 LMCache는 "빨리 읽는 게 빨리 쓰는 것보다 중요" 라는 철학. TTFT 최소화가 최우선이니 당연. → 우리도 FDP 스트림 우선순위/QoS 설계 시 같은 원칙 적용 가능. ([[기여-포인트-맵|기여 포인트 [9]]])


6. 핵심 한계 — "진짜 async I/O가 아니다"

Async Loading Layer
└── batched_get_non_blocking() ← non-blocking 요청만 던짐

└── LocalDiskBackend.get() ← 실제 파일 읽기 (blocking)
│ 별도 스레드(LocalDiskWorker)에서 실행
└── .pt 파일 read 되니까 호출자는 안 기다림

LocalDiskBackend.get() 자체는 여전히 blocking syscall. 단지 별도 스레드에서 돌릴 뿐.

[!important] [[io_uring]] 도입 시 효과

  • 스레드 컨텍스트 스위치 제거
  • syscall 한 번에 다수 I/O batch
  • FDP placement hint를 io_uring_cmd로 직접 전달 가능 ([[NVMe-FDP|FDP]] passthru 경로)
  • 스레드 풀 자체 불필요해질 수 있음

7. 실측 예시 (LMCache 문서 — 15,376 토큰 프롬프트)

  • Cold TTFT (캐시 없음): 6.314초
  • Warm TTFT (캐시 있음): 0.148초
  • 42.6× 빠름

우리 질문: FDP/HC-SSD로 0.148초를 더 줄일 수 있는가?


8. I/O 경로 추적 — Storage Stack 관점

LMCache LocalDiskBackend
│ .pt 파일 read/write (torch.save/load)
│ ⚠️ 정확한 syscall 종류 (read/pread/io_uring) — 코드 레벨 확인 필요

File System (ext4 / xfs / f2fs) ← [[기여-포인트-맵|포인트 [4]]]
│ VFS Layer

Block Layer (I/O Scheduler) ← [[기여-포인트-맵|포인트 [6][7]]]
│ bio 요청

NVMe Driver ← [[기여-포인트-맵|포인트 [8]]]

NVMe SSD (FDP/HC-SSD) ← [[기여-포인트-맵|포인트 [9][10][11]]]

9. FDP 관점 핵심 요약

발견FDP 적용 포인트
청크 하나 = 파일 하나medium file write 패턴 → FDP 스트림 분리 효과 큼
O_DIRECT 옵션 존재Page Cache 우회 → 실험 정확도 ↑
GET은 blockingread 레이턴시 개선이 TTFT에 직접
PUT은 async (burst)write 집중 → [[WAF]] 개선 여지
LRU evict = 파일 삭제delete + write 반복 → GC pressure
256-token chunk sizeNVMe optimal I/O size와 정렬 가능성 검토

관련 페이지

  • [[LMCache-아키텍처]] — 상위 페이지
  • [[LMCache-Async-Loading]] — 이 백엔드를 비동기로 부르는 상위 레이어
  • [[O_DIRECT]] — use_odirect 옵션 이해
  • [[io_uring]] — 진짜 async I/O로 교체할 후보
  • [[NVMe-FDP]] — RUH/RU 매핑 설계 대상
  • [[기여-포인트-맵]] — [1][2][3][5][8][9] 포인트가 이 페이지의 코드를 건드린다