아키텍처_리뷰
┌─────────────────────────────────────────┐
│ Application (User) │
└──────────────────┬──────────────────────┘
│ HTTP Request
┌──────────────────▼──────────────────────┐
│ vLLM (Inference Engine) │
│ ┌─────────────┐ ┌────────────────┐ │
│ │ Scheduler │ │ GPU Worker │ │
│ └──────┬──────┘ └───────┬────────┘ │
└─────────┼───────────────────┼───────────┘
│ (LMCache 연동) │
┌─────────▼───────────────────▼───────────┐
│ LMCache Layer │
│ ┌──────────────────────────────────┐ │
│ │ Cache Index (Token DB) │ │
│ └──────────────────────────────────┘ │
│ ┌──────────────────────────────────┐ │
│ │ Storage Manager │ │
│ └───┬──────────────┬───────────────┘ │
│ │ │ │
│ ┌───▼───┐ ┌───▼──────────┐ │
│ │ CPU │ │ LocalDisk │ │
│ │ DRAM │ │ Backend │ │
│ └───────┘ └───┬──────────┘ │
└────────────────────┼───────────────────┘
│
┌────────────────────▼───────────────────┐
│ Storage Stack │
│ ┌──────────────────────────────────┐ │
│ │ File System (ext4/xfs) │ │
│ └──────────────────────────────────┘ │
│ ┌──────────────────────────────────┐ │
│ │ Block Layer (I/O Scheduler) │ │
│ └──────────────────────────────────┘ │
│ ┌──────────────────────────────────┐ │
│ │ NVMe Driver │ │
│ └──────────────────────────────────┘ │
│ ┌──────────────────────────────────┐ │
│ │ NVMe SSD (FDP/HC-SSD) │ │
│ └──────────────────────────────────┘ │
└────────────────────────────────────────┘
리뷰목적-> LMCache가 NVMe에 어떤 방식으로 I/O를 내려보내는지 이해하기 위함
Architecture Overview Review LMCache 전체 구조(LMCache가 어떻게 생겼지?)
Q1. LMCache의 4개 스토리지 티어를 빠른 순서대로 나열해볼 수 있어? 그리고 각각 어떤 역할인지 한 줄씩 말해봐.
GPU Memory, CPU DRAM, Local storage, Remote storage GPU Memory - 현재 추론에 실제로 사용 중인 'Active Working Set.' CPU DRAM - Hot cache 역할. GPU Memory overflow 시 offloading, GPU<->CPU 전송 속도를 높이기 위해 Pinned Memory로 씀 Local Storage - CPU DRAM에서 다 못담는 대용량 KV Cache를 위한 확장 티어. LRU 정책에 의해 Local storage에 kv cache 저장됨. Remote Storage - 영구 보관. 용량 큼. 가장 느림.
Q2. KV Cache가 NVMe에 write되는 건 어떤 상황에서 발생해? (힌트: 어떤 컴포넌트가 트리거야?)
CPU DRAM이 가득 참 -> Memory Allocator가 LRU Eviction 실행 (Q. DRAM이 가득 찬거 어떤방식으로 캐치?) -> Async Offloading이 비동기로 NVMe write 즉 [Memory allocator]가 판단. [Async offloading]이 실제로 nvme write
Q3. Storage Mode랑 Transport Mode의 차이를 한 문장으로 설명해봐.
Storage Mode는 KV Cache를 여러 계층에서 저장, 즉 KV Cache를 재활용 하는 것이고, Transport Mode는 Prefill과 Decode 가 다른 서버에서 이루어질때 KV cache를 전송하는 모드이다.
Q4. Cache Index가 256토큰 단위로 청크를 나눈다고 했는데, 이게 FDP 설계할 때 왜 중요한 정보야?
청크단위로 storage에 전송되니까! FDP에는 RU (Reclaim Unit)이라는 개념이 있다. SSD가 GC할 때 한 번에 정리하는 단위. 이 크기랑 write단위가 안 맞으면 불필요한 데이터까지 같이 지워지고 다시 써야해서 WAF가 올라감 즉, LMCache write단위와 RU크기를 맞춰주면 GC효율이 올라감.
256토큰 청크 단위로 NVMe에 I/O가 발생하기 때문에, FDP의 RU 크기를 이 청크 크기에 맞게 정렬하면 WAF를 줄이고 GC 효율을 높일 수 있다
Q5. Controller의 API 중에서 FDP ON/OFF 비교 실험할 때 제일 유용하게 쓸 수 있는 API는 뭐고, 왜 그렇게 생각해?
실험 전 → 이전 캐시 데이터가 남아있으면 안 되잖아. 깨끗하게 초기화해야 해 실험 중 → NVMe에 KV Cache가 실제로 내려갔는지 확인해야 해 실험 후 → 비동기 write가 다 끝난 시점을 알아야 정확한 측정이 가능해
이걸 Controller API랑 연결하면: | 상황 | 필요한 API | | 실험 전 캐시 초기화 | Clear | | NVMe에 캐시 있는지 확인 | Lookup | | 비동기 write 완료 확인 | Health & Finish Check | | 특정 캐시 강제로 NVMe로 내려보내기 | Move |
비동기 write이므로 write 발생과 write 완료 시점이 다름 *
========================================================================================
Storage backend(local) Review LMCache <-> Storage 경계 (I/O가 어떻게 NVMe로 내려갈까?)
Q1. Local Storage에서 KV Cache 청크 하나가 디스크에 어떤 형태로 저장돼? 그리고 이게 FDP 설계할 때 왜 문제가 될 수 있어? 파일1개. 256token = 1chunk = 1file 대량의 small I/O 발생
Q2. use_odirect 옵션이 뭐고, 우리가 FDP 실험할 때 이걸 켜야 하는 이유가 뭐야? OS의 Page Cache를 거치지 않고 NVMe (ssd)에 직접 I/O하는 것. 정확도. Page cache를 거치면 NVMe를 거치지 않고 cache hit 발생할 수도 있으니까 정확도가 떨어짐.
CPU DRAM ├── LMCache가 관리하는 영역 → KV Cache 저장 (Pinned Memory) └── OS가 관리하는 영역 → Page Cache (파일 읽기/쓰기 임시 버퍼) 둘 다 물리적으로는 같은 DRAM 칩이지만 누가 관리하느냐가 달라.
LMCache CPU DRAM = LMCache가 직접 할당해서 KV Cache를 담는 공간 Page Cache = OS가 파일 I/O를 빠르게 하려고 자동으로 쓰는 버퍼
Q3. put(), get(), prefetch() 세 가지 오퍼레이션의 차이를 설명해봐. 특히 블로킹/비블로킹 관점에서!
put() : write. cpu dram이 부족할 때 비동기적으로 이루어짐. get() : read. blocking으로 이루어짐. 지금 당장 필요한 데이터기 때문에! 추론이 이 데이터를 받아야 진행할 수 있으니까 기다릴 수 밖에 없음 -> 결과적으로 TTFT에 영향을 줌 prefetch() : storage에 있는 걸 cpu로 미리 불러오는 것. 비동기적으로 이루어짐.
Q4. LocalDiskWorker의 우선순위가 PREFETCH > DELETE > PUT 순서인데, 왜 이런 순서로 설계했을까?
read가 가장 중요함 왜냐면 TTFT 최소화 목적. 빨리 불러와야 빨리 디코딩 하고 사용자에게 빨리 응답할 수 있음.
Q5. Multi-Path 설정이 뭔지 설명하고, FDP 검증 실험할 때 이걸 어떻게 활용하면 좋을지 말해봐.
multi device일 때 특정 gpu에 특정 disk를 할당할 수 있는 설정. GPU와 disk가 맵핑되어있으니까 kv cache 데이터가 흩어지지 않아서 분석하기 쉽다? 각 NVMe에 I/O가 섞이지 않으므로
- WAF 측정이 정확해짐
- I/O 패턴 분석이 GPU별로 깔끔하게 나뉨
- FDP 스트림이 다른 GPU의 Write에 오염되지 않음.
"GPU마다 NVMe를 1:1로 매핑해서 KV Cache I/O가 섞이지 않게 분리할 수 있어. FDP 실험할 때 GPU별로 FDP ON/OFF를 나눠서 동시에 비교 실험하거나, WAF·I/O 패턴을 GPU 단위로 정확하게 측정할 수 있어."
========================================================================================
Async Loading Review LMCache 내부 I/O 동작 (I/O 패턴이 어떻게 생겼는지)
Q1.async_loading의 핵심 목적이 뭐야? 한문장으로 설명하면?
GPU를 놀지 않게 하려고 GPU 추론 연산과 데이터를 가져오는거랑 동시에 할 수 있게 함. "Scheduler 측 lookup"과 "Worker 측 prefetch / retrieval"을 분리. 이 분리 덕분에 overlap이 가능.
Q2.Scheduler랑 Worker가 어떻게 통신해? 그리고 Worker는 LMCache의 어떤 컴포넌트야?
ZMQ((ZeroMQ) = 빠른 메시지 전송을 위한 통신 라이브러리) 비동기로 통신. Worker는 KV Cache I/O요청을 받아서 백엔드에 위임하고 결과를 돌려주는 중간 서버
Q3. 캐시 히트가 났을 때랑 미스났을 때 각각 어떤 일이 벌어져?
lookup -> Cache hit 발생하면 -> 결과 전달 lookup -> Cache miss 발생하면 -> 생성된 chunk 저장
Q4. LocalDiskWorker의 현재 구조가 진짜 async I/O가 아니다 라고 했는데, 왜 그렇고 io_uring으로 바꾸면 뭐가 좋아져?
Q5. FDP/HC-SSD Backend를 새로 만들 때 async_loading을 지원하려면 반드시 구현해야 하는 인터페이스가 뭐야? 그리고 왜 그게 필요해?