LabHub

한국어

시작하기
블로그

블로그

AI 스토리지와 KV 캐시 오프로드: 가중치 로딩 경로부터 LMCache 실측까지

상태: 초안 (2026-10-07 갱신), 일부 실측 반영. 공식 문서로 확인한 서술이 바탕이고, 7절의 KV 오프로드 실측(8GB급 GPU 한 장)과 「외부 근거」 절의 논문 대조가 더해졌습니다. 저장소 성능(VAST, 3FS, GDS)은 직접 재지 못해 벤더 자료와 논문의 조건을 확인한 수준입니다. 글 끝의 「확인한 버전과 날짜」와 「확인하지 못한 것」을 먼저 읽으십시오. 작성 규칙: 실측 데이터가 기본이고, 못 잰 것은 「미실측」으로 표시합니다.

AI 스토리지와 KV 캐시 오프로드

대형 모델 서빙에서 GPU 계산만큼 자주 문제가 되는 것은 두 가지입니다. 수백 GB 가중치를 스토리지에서 GPU 메모리까지 옮기는 시간, 그리고 GPU 메모리에 다 들어가지 않는 KV(Key-Value) 캐시를 어디에 둘 것인가입니다. 이 글은 두 문제를 저장 계층(객체 저장소, 파일 시스템, 로컬 NVMe, 페이지 캐시, GPU 메모리)으로 묶어 설명합니다. 필자의 실측은 규모가 작으므로 한계까지 함께 적었습니다.

이 글을 읽고 나면 면접에서 설명할 수 있는 것

  1. 파드 수백 개가 동시에 뜰 때 로딩이 병목이 되는 이유와, 로더 접근 패턴, 파일 시스템 배치, 캐시 계층 가운데 무엇부터 고쳐야 하는지.
  2. VAST Data 같은 AI 스토리지가 해결하는 문제, GPUDirect Storage(GDS)의 정체, 그리고 벤더 수치 중 「주장」으로 분리해야 할 부분.
  3. KV 캐시 오프로드가 이득이 되는 조건을 식으로 세우고, 필자의 실측을 대입해 손익분기 적중률을 계산하는 방법.

1. 왜 로딩이 병목인가

가중치 크기는 매개변수 수 곱하기 자료형 바이트입니다. 700억(70B) 매개변수를 bf16(2바이트)으로 두면 140GB입니다(계산). 파드마다 같은 파일을 읽으므로 100개가 동시에 뜨면 읽어야 할 총량은 14TB가 됩니다. 스토리지 집계 대역폭이 10GB/s라고 가정하면 읽기만 약 1,400초(23분)입니다(둘 다 설명용 예시이며 실측이 아닙니다).

오토스케일에서는 이 시간이 곧 반응 지연입니다. 파드가 스케줄되고, 이미지를 받고, 가중치를 올리고, 컴파일과 워밍업을 마쳐야 readiness 가 통과됩니다. 로딩이 10분이면 부하가 오른 뒤 10분 동안 늘어난 파드는 요청을 받지 못하므로, 로딩 시간은 사전 스케일, 여유 용량, 로딩 경로 개선의 선택을 모두 좌우합니다.

NVIDIA Dynamo 문서에는 실측 사례가 있습니다. 554GiB 모델을 8×H100 노드 두 대(텐서 병렬 16)에서 Azure Managed Lustre(OST 4개, 집계 약 8GB/s)로 올렸더니 기본 로더는 유효 처리량이 약 124MB/s였고 로딩에 약 76분이 걸렸습니다. 로더를 바꾸고 파일을 모든 OST(Object Storage Target)에 분산 배치(striping)하자 약 5.3분이 되었습니다. 한 환경에 대한 벤더 측정입니다. 요점은 스토리지가 높은 대역폭을 가져도 읽는 쪽의 접근 패턴이 나쁘면 그것을 쓰지 못한다는 것입니다.

2. 로딩 경로의 계층

 객체 저장소 (S3 호환)  ─┐
 병렬/분산 파일 시스템   ─┼─▶ 노드 로컬 NVMe ─▶ 페이지 캐시 ─▶ CPU 버퍼 ─▶ GPU HBM
 (Lustre, VAST, WEKA 등) ─┘   (hostPath/로컬PV)   (DRAM, mmap)   (핀드 메모리)  (샤드별 적재)
   용량 큼, 공유, 느림  ◀──────────────────────────────────────▶  작고 빠름, 노드/GPU 전용
계층 특성 흔한 실패
객체 저장소 용량이 크고 모든 노드가 공유합니다. 요청당 지연이 크므로 큰 범위를 병렬로 읽어야 합니다. 파드 수백 개의 동시 GET 이 한 엔드포인트에 몰립니다.
병렬/분산 파일 시스템 여러 스토리지 노드에 데이터를 분산합니다. 배치(striping)에 따라 한 파일의 읽기 상한이 정해집니다. 파일이 대상 하나에만 놓여 집계 대역폭을 쓰지 못합니다.
노드 로컬 NVMe 캐시 네트워크 경쟁이 없습니다. 노드마다 사본이 하나씩 필요합니다. 노드가 바뀌면 다시 받습니다.
페이지 캐시 같은 노드의 파드들이 같은 파일을 DRAM 에서 읽습니다(운영체제 일반 동작). 컨테이너 메모리 한도에 더티 페이지가 포함되어 OOM 이 납니다(7절).
GPU HBM(High Bandwidth Memory) 최종 목적지입니다. 텐서 병렬이면 각 GPU 가 자기 샤드만 올립니다. 로딩 중 CPU 버퍼가 부족합니다.

3. 모델 파일 형식과 로딩 최적화

safetensors 형식. 앞 8바이트(리틀 엔디언 부호 없는 64비트)가 헤더 크기 N 이고, 다음 N바이트가 텐서 이름, 자료형, 모양, 데이터 오프셋을 담은 JSON 헤더이며, 나머지가 텐서의 원시 바이트입니다. pickle 과 달리 코드를 실행하지 않아 안전하고, 헤더만 읽고도 특정 텐서의 위치를 알 수 있어 일부만 읽는 지연 로딩이 됩니다. 텐서 병렬에서는 get_slice 로 자기 몫의 조각만 읽을 수 있습니다. 다만 프로젝트 README 도 밝히듯 CPU 에서는 캐시된 파일에 한해 복사 없이 쓸 수 있어도, GPU 로 가려면 복사가 반드시 한 번 필요합니다. 안전하다는 말은 코드 실행에 대한 것이고, 텐서 값(NaN 등)이 올바른지까지 검사하지는 않습니다.

기본 로더의 한계. Dynamo 문서에 따르면 vLLM 기본 로더(--load-format auto)는 각 safetensors 샤드를 mmap 하고 단일 스트림으로 복사합니다. 원격 스토리지에서는 작은 요청이 얕은 큐 깊이로 나가 지연에 묶입니다. 같은 문서의 Lustre 측정에서 단일 대상 읽기는 블록 128KiB 에서 약 228MB/s, 1MiB 에서 약 854MB/s, 16MiB 에서 약 1.6GB/s 였습니다.

스트리밍 로더. vLLM 문서의 Run:ai Model Streamer 는 pip install vllm[runai] 후 --load-format runai_streamer 로 켭니다. --model-loader-extra-config 로 concurrency(읽기 스레드 수), memory_limit(CPU 버퍼 크기), distributed(객체 저장소에서 분산 스트리밍)를 조절하고 로컬 경로, S3, GCS, Azure Blob 을 읽습니다. 모델이 model-rank-{rank}-part-{part}.safetensors 로 미리 쪼개져 있으면 runai_streamer_sharded 로 각 워커가 자기 샤드만 읽습니다. 이 프로젝트는 2026-09-13 에 Model Streamer 로 이름이 바뀌고 dsx-ai-factory/model-streamer 로 옮겨졌으나 PyPI 패키지 이름은 그대로입니다.

그 밖에 vLLM 문서에는 GDS 를 쓰는 fastsafetensors, CoreWeave Tensorizer, 재시작 때 GPU 에 남은 가중치를 CUDA IPC 로 다시 매핑하는 vllm preload가 있습니다. Dynamo 의 ModelExpress 는 이미 서빙 중인 복제본의 GPU 에서 RDMA(Remote Direct Memory Access)로 가중치를 받으며, README 는 8×B200 노드, vLLM 0.23.0, 텐서 병렬 8 조건에서 콜드 풀 8분 53초 대비 11초(48배)라고 주장합니다(벤더 수치).

4. 고성능 AI 스토리지: VAST Data 를 중심으로

VAST 백서에 따르면 구조는 DASE(Disaggregated Shared-Everything)입니다. 상태가 없는 CNode(컨테이너로 도는 프로토콜과 로직 서버)가 모든 SSD 를 NVMe-oF(NVMe over Fabrics)로 마운트하고, 데이터는 DNode/DBox 의 SCM(쓰기 버퍼)과 QLC 플래시에 놓입니다. 접근 프로토콜은 NFS(RDMA 위의 NFS 포함), S3, SMB, 블록 등입니다. NVIDIA GDS 문서는 GDS 를 쓸 수 있는 네트워크 파일 시스템으로 DDN-EXAScaler, VAST-NFS, WekaFS 를 나열하고 Lustre 도 지원 대상에 둡니다.

GDS 는 스토리지에서 GPU 메모리로 CPU 바운스 버퍼 없이 DMA(Direct Memory Access)로 보내는 경로입니다. 조건이 맞지 않으면(nvidia-fs 드라이버 부재, 파일 시스템 미지원, O_DIRECT 불가) 호환 모드로 CPU 메모리를 거치는 경로로 내려갑니다. 따라서 「GDS 지원」은 켜졌다는 뜻이 아니라 켤 수 있다는 뜻이고, 실제 경로는 확인해야 합니다.

벤더 주장으로 분리해야 할 수치

대안 비교(간단히). Lustre 는 오픈소스 병렬 파일 시스템이며 배치(stripe_count)가 성능을 좌우합니다. DeepSeek 의 3FS 는 NVMe 와 RDMA 기반 분산 파일 시스템(MIT 라이선스)이고 메타데이터를 FoundationDB 에 두며, README 는 180노드에서 약 6.6TiB/s 읽기를 주장합니다(저장 노드 180대와 클라이언트 500대 이상, 배경 학습 트래픽이 있는 읽기 스트레스 시험이며 저장 노드 NIC 합계의 약 81% 라는 조건이 README 원문에 적혀 있습니다). WEKA 와 DDN 은 GDS 문서의 목록 외에 공식 자료를 열어 확인하지 못했습니다.

5. 쿠버네티스에서의 스토리지

6. KV 캐시 오프로드

        GPU HBM (G1)  ◀──────────────▶  CPU 핀드 메모리 (G2)
        활성 KV, 가장 빠름     DMA, 비동기       │  ▲
                                                 ▼  │ 선인출(prefetch)
                              로컬 NVMe/디스크 (G3) ◀──▶ 원격/객체 저장소 (G4)
   적중 시 위로 올림(promote), 넘치면 아래로 내림(LRU 등), GPU 와 직접 닿는 층은 CPU 층뿐

계층 이름은 Dynamo KVBM 문서(G1 GPU HBM, G2 핀드 DRAM, G3 NVMe/SSD, G4 S3/MinIO)를 따랐습니다. vLLM 의 OffloadingConnector 도 CPU 층만 GPU 와 직접 닿고 보조 층은 CPU 를 거치는 같은 구조입니다.

LMCache 의 구조. LMCache 는 KV 캐시 관리 계층이고, 현재 문서는 두 모드를 구분합니다. 원래 방식인 프로세스 내(in-process) 모드는 vLLM 안에서 LMCacheConnectorV1 로 동작하며 문서가 deprecated 로 표시합니다. 신규 배포는 멀티프로세스(MP) 모드를 권하며, 독립 lmcache server 가 노드마다 하나(DaemonSet) 떠서 여러 vLLM 파드가 LMCacheMPConnector 로 공유합니다. 서버는 L1(CPU DRAM 또는 GDS NVMe 슬랩)과 L2(파일 시스템, NIXL, S3, Redis 등)를 두고, L1 에서 L2 로는 비동기로 밀어 넣고 미적중 때 L2 에서 L1 로 올립니다. 문서가 드는 장점은 vLLM 프로세스의 GIL 경합 제거와 장애 격리입니다. 쿠버네티스 오퍼레이터는 LMCacheEngine 사용자 정의 리소스(lmcache.lmcache.ai/v1alpha1) 하나로 DaemonSet, Service, ConfigMap 을 조정합니다.

인스턴스 사이 접두사 공유. 방법은 넷입니다. 같은 노드의 파드가 MP 서버의 L1 을 공유하거나, L2 를 공유 파일 시스템이나 S3 로 두거나(어댑터의 "shared": true), vLLM 자체의 fs 티어를 공유 PVC 의 같은 root_dir 로 지정하거나(해시 시드가 고정이라 같은 파일 이름이 나오며, 일부 해시 알고리즘은 PYTHONHASHSEED 를 맞춰야 합니다), RDMA P2P 공유(코디네이터와 RDMA 망이 필요하고 GDS L1 과는 함께 쓸 수 없음)를 씁니다. 요청이 캐시를 가진 인스턴스로 가야 이득이므로, KServe 문서의 스케줄러는 vLLM 이 보내는 BlockStored/BlockRemoved 이벤트로 색인을 만들어 접두사 적중 점수(가중치 2.0)와 부하 점수(1.0)로 라우팅합니다.

비용 대 이득. 토큰당 KV 크기는 2 × 층 수 × KV 헤드 수 × head_dim × 자료형 바이트입니다. 필자의 실험 모델 Qwen2.5-1.5B(Hugging Face config: 28층, KV 헤드 2, head_dim 1536/12=128, fp16)는 토큰당 28,672바이트이고, 4,864토큰이면 139,460,608바이트(0.1299GiB)입니다. LMCache 로그의 0.1299 와 일치합니다. 70B 급 Llama(80층, KV 헤드 8, head_dim 128, bf16)는 토큰당 327,680바이트이므로 3만 2천 토큰이면 약 10.7GB 입니다(계산). 적중 한 번의 이득은 T_재계산 − (S/B + 지연) 이고, 적중 확률 p 의 평균 이득 p×이득 − (1−p)×저장비용 이 양수여야 합니다. 저장 계층의 대역폭 B 가 낮으면 S/B 가 재계산을 넘어섭니다.

7. 직접 잰 실측과 흔한 함정

KV 오프로드 실측. 8GB급 노트북 GPU 에서 Qwen2.5-1.5B-Instruct(fp16)를 vLLM 0.29.0 과 LMCache 0.5.5 로 올렸고, GPU KV 캐시 크기는 두 설정 모두 21,520 토큰이었습니다. 문서 10개(문서당 약 5,011 토큰, 합계 약 5만 토큰)를 첫 읽기, 같은 순서로 다시 읽기, 거꾸로 읽기로 한 요청씩 보냈습니다. LMCache 는 청크 256, CPU 상한 1.5GB, 프로세스 내 모드(LMCacheConnectorV1)였습니다.

설정 패스 TTFT p50 (ms)
vLLM 만 처음 575.5
vLLM 만 같은 순서로 다시 576.2
LMCache 처음 597.8
LMCache 같은 순서로 다시 48.6

실험의 한계. 동시성이 1 이고, 모델이 1.5B 로 작고, 문서는 난수 낱말로 만든 합성 문서이고, GPU 는 한 장입니다. 한 번 잰 값이라 표준편차를 구하지 않았고, 이미지 태그가 latest 라 재현하면 버전이 달라질 수 있습니다. 프로세스 내 모드는 현재 LMCache 문서에서 deprecated 이므로 MP 모드의 수치는 재측정하지 않았습니다. 큰 모델에서 격차가 더 커질 것이라는 기대는 이 측정이 보여 주는 것이 아닙니다.

심화 실측: 용량 곡선, 길이별 이득, 디스크 계층, 청크 크기

위 실험을 같은 환경(8GB급 GPU 한 장, vLLM 0.29.0, LMCache 0.5.5, Qwen2.5-1.5B-Instruct)에서 조건을 바꿔 다시 했습니다. 이번에는 GPU KV 풀을 21,520 토큰으로 고정했고(서버를 다시 띄워도 같은 값), 문서는 10개(문서당 5,189 토큰, 합 52,025 토큰)입니다. 이 작업 집합의 KV 크기는 토큰당 28,672바이트(28층 × KV 헤드 2 × 헤드 128 × 키와 값 2 × 2바이트)로 계산해 약 1.49GB 입니다.

CPU 캐시 용량 곡선. 같은 순서로 다시 읽을 때(LRU 최악)의 TTFT 중앙값입니다.

LMCache CPU 상한 다시 읽기 TTFT 외부 적중 토큰 / 질의 토큰
없음(vLLM 만) 600ms 해당 없음
0.5GB 620ms 0 / 52,025
1.0GB 620ms 0 / 52,025
1.5GB 39ms 51,200 / 52,025 (98%)
2.0GB 39ms 51,200 / 52,025 (98%)

실제 접근처럼 쏠린 경우(문서 60개, 지프 분포 s=1, 요청 60개). 일부 문서가 자주 읽히면 작은 캐시도 도움이 됩니다.

설정 TTFT 평균 외부 적중 토큰 비율
vLLM 만 278ms 해당 없음
CPU 0.5GB 289ms 0%
CPU 1.0GB 174ms 44%
CPU 1.5GB 121ms 64%
CPU 2.0GB 63ms 87%
CPU 0.2GB + 디스크 10GB 47ms 98%

중앙값은 대부분 30ms 안팎이어서(자주 읽히는 문서는 GPU 에서 바로 적중) 평균과 p95 가 용량 효과를 더 잘 보여 줍니다.

길이별 이득(CPU 2GB, GPU 에서 밀려난 뒤 같은 문서를 다시 요청).

입력 토큰 재계산(vLLM 만) LMCache 에서 불러옴 배율
554 66ms 21ms 3.1배
1,077 119ms 24ms 5.0배
2,144 231ms 29ms 8.0배
4,273 481ms 45ms 10.7배
6,414 768ms 41ms(36~43) 약 19배

재계산 시간은 길이에 거의 비례해 늘지만 불러오는 시간은 훨씬 완만하게 늘어, 문맥이 길수록 이득이 큽니다. 같은 길이의 GPU 적중(20~32ms)보다 불러오기가 1~17ms 더 걸리는 수준입니다.

디스크 계층. CPU 0.2GB 와 로컬 디스크 10GB 를 쓰면 다시 읽기가 57ms(p95 255ms)였고 CPU 만 쓸 때(39ms)보다 느렸지만 재계산(600ms)보다 훨씬 빨랐습니다. 파일이 방금 쓴 것이라 운영체제 페이지 캐시의 도움을 받았을 수 있어, 차가운 디스크의 값이라고 읽으면 안 됩니다(미분리).

청크 크기(64, 256, 1,024). 공통 접두사 2,000 토큰에 꼬리 1,000 토큰을 붙인 문서로 접두사 재사용을 재 보았더니, 세 청크 크기 모두 외부 적중이 2,048 토큰, TTFT 가 140~144ms 로 같았습니다. 이 시험에서 청크 크기의 영향은 보이지 않았습니다. 다만 이 시험의 접두사가 이미 GPU 접두사 캐시에도 남아 있어 vLLM 만 쓴 값(138ms)과도 같았으므로, 청크 정렬 효과를 가려내는 시험으로는 부족합니다(미분리).

한계. 서버 상태를 바꿔 가며 한 번씩 쟀고(청크와 용량 곡선은 반복하지 않음), 동시성 1 이며, 합성 문서와 1.5B 모델입니다. 같은 조건에서 위 7절 첫 실험을 다시 했을 때 처음 읽기 598에서 618ms, 다시 읽기 48.6에서 39ms 로 약간 달랐으므로 한 자릿수 ms 차이는 의미를 두지 않아야 합니다.

흔한 함정: 더티 페이지와 메모리 한도. 네트워크 파일 시스템 위에 큰 객체를 올릴 때, 아직 저장소로 나가지 못한 더티 페이지 캐시가 컨테이너 메모리로 계산되어 메모리 한도를 넘고 OOMKilled 되는 경우가 있습니다. 동시 업로드 수를 제한하고 메모리 한도를 늘려 해결한 사례를 겪었습니다. 페이지 캐시 계층이 메모리 한도와 얽힌 예입니다.

없는 경험. 저는 VAST, WEKA, GDS 를 운영해 본 적이 없습니다. 4절은 공식 자료와 벤더 문서를 읽고 정리한 것입니다.

외부 근거: 논문과 공식 자료로 확인한 것

KV 오프로드와 저장소 서술을 1차 자료와 대조했습니다. 외부 수치는 모델, GPU, 부하가 달라 직접 비교하지 않고 방향과 자릿수의 일치만 판정했습니다.

주제 자료와 확인한 위치 확인한 내용과 조건 이 글과의 관계
LMCache 의 이득 Liu 외, 「LMCache: An Efficient KV Cache Layer for Enterprise-Scale LLM Inference」, arXiv 2510.09665(2025-12 개정), 8.2·8.4·8.7·9절 8×H100, Llama-3.1-8B 등 8B~480B 모델, CPU 500GB. 다회전 문서 질의응답에서 QPS 1 의 TTFT 1.9~8.1배 감소, 같은 TTFT 에서 처리량 2.3~14배(초록의 「최대 15배」는 본문에서 14배로만 확인됨). 원격 저장소는 CPU 보다 대역폭이 낮아 입력이 짧으면 불러오기가 프리필보다 느릴 수 있음 제한적으로 지지. 방향과 자릿수는 같음. 지표(부하 아래 TTFT 대 동시성 1 의 적중 대 미적중)와 기준선이 달라 우리 12배와 직접 비교 못 함. 저장 비용은 논문이 별도 수치로 보고하지 않음
가까운 독립 측정 py-kvcache 논문(arXiv 2609.11744) 같은 문서를 반복해 출력 1토큰으로 적중 TTFT 만 잰 시험에서 재계산 대비 1k 토큰 2.2배, 80k 토큰 32.8배(논문이 쓴 워크스테이션급 GPU 한 장, PCIe 4.0 x16, vLLM 0.16, CPU 계층) 지지. 우리 5천 토큰 약 12배는 이 범위 안
KV 압축 전송 Liu 외, CacheGen, SIGCOMM 2024. Yao 외, CacheBlend, EuroSys 2025 CacheGen: KV 크기 3.5~4.3배 감소, 정확도 하락 2% 이하, 저대역폭에서는 불러오기가 재계산만큼 느려짐을 인정. CacheBlend: 접두사가 아닌 재사용으로 TTFT 2.2~3.3배, 품질 하락 0.02 이내 CacheGen 제한적으로 지지(손해 조건), CacheBlend 는 관련 없음(다른 문제)
손익분기 식 그대로 쓴 자료는 찾지 못함. CacheGen 부록 E(월 요청 수 손익분기, 원문 산술이 맞지 않아 인용하지 않음), VAST·Lablup 공동 블로그(2026-06-24) 손익분기가 길이, 저장 대역폭, 적중률에 의존한다는 서술. VAST·Lablup 수치로는 4% 가 아닌 약 18~21% 제한적으로 지지. 4% 는 일반 상수가 아님(글이 이미 밝힘)
PCIe 이론 대역폭 NVIDIA 제품 자료와 Rambus 해설(PCI-SIG 사양서는 접근이 막혀 열지 못함) 4.0 x16 약 29.3GiB/s, x8 약 14.7GiB/s, 3.0 x16 약 14.7GiB/s 조건부. 링크 폭에 따라 한계 안일 수도, 초과할 수도 있어 위 서술을 「이론 한계와 같은 수준」으로 고침
3FS DeepSeek 3FS README 180 저장 노드, 클라이언트 500대 이상, 배경 학습 트래픽, 약 6.6TiB/s(NIC 합계의 약 81%) 지지. 조건을 본문에 보강
GDS NVIDIA GPUDirect Storage 공식 문서 일부 시스템에서 최대 대역폭이 적어도 두 배라고만 서술, 나머지 수치는 협력사 제공 지지(정성적). DDN 보도자료는 조건이 없고, WEKA 최근 수치는 원문을 열지 못해 쓰지 않음
가중치 로딩 시간 Fu 외, ServerlessLLM, OSDI 2024 PyTorch 로 OPT-30B 를 올리는 데 34초, LLaMA-2-70B 는 84초(그림 기준). 가장 작은 모델이 2.7B 제한적으로 지지. 1.7B 의 4~6초와 맞댈 수치는 본문에 없음
safetensors safetensors README, Python pickle 문서와 PyTorch torch.load 경고 헤더 구조와 GPU 로 가려면 복사가 한 번 필요하다는 서술 일치, pickle 은 임의 코드 실행 위험 지지. 값(NaN 등)을 검사하지 않는다는 한정을 본문에 보탬

8. 직접 해 보기

모두 읽기 전용이거나 임시 파드로 끝납니다.

# (1) 모델 설정만으로 토큰당 KV 크기 계산: 로컬 실행, 환경 변경 없음
python3 - <<'PY'
import json, urllib.request
u = "https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct/resolve/main/config.json"
c = json.load(urllib.request.urlopen(u))
hd = c.get("head_dim") or c["hidden_size"] // c["num_attention_heads"]
b = 2 * c["num_hidden_layers"] * c["num_key_value_heads"] * hd * 2   # fp16/bf16
print(b, "B/token;", b * 4864 / 2**30, "GiB per 4864 tokens")
PY

# (2) 블록 크기에 따른 읽기 처리량: 기존 큰 파일을 읽기만 합니다(쓰기 없음)
for bs in 128k 1m 16m; do
  fio --name=r$bs --filename=/mnt/models/큰파일.safetensors --rw=read \
      --bs=$bs --direct=1 --size=4g --readonly | grep -E "READ:"
done

(3) 실험 재현. GPU 를 쓰는 임시 파드 하나에서 vLLM 과 LMCache 를 돌리고, 끝나면 파드를 지웁니다. 노드 메모리를 다른 워크로드와 나눠 쓴다면 파드의 메모리 한도를 함부로 키우지 마십시오.

9. 면접에서 나올 만한 질문

  1. 수백 개 파드가 동시에 뜰 때 모델 로딩을 어떻게 설계하겠습니까? 총 읽기량(파드 수×크기)을 먼저 계산하고, 읽는 쪽은 스트리밍 로더로 큰 블록을 동시에 읽게 하며, 저장 쪽은 파일 배치와 노드 로컬 캐시, 복제본 간 전송으로 공유 스토리지 부하를 줄입니다. 검증은 단일 파드가 아니라 실제 규모의 스케일 아웃 시험으로 합니다.
  2. 텐서 병렬에서 샤드별 로딩이 가능한 이유는 무엇입니까? safetensors 의 헤더에 텐서별 오프셋이 있어 필요한 바이트 범위만 읽을 수 있고, 미리 랭크별로 쪼갠 파일이면 각 워커가 자기 파일만 읽어 전체 읽기량이 줄기 때문입니다.
  3. VAST 를 도입하면 로딩과 KV 오프로드가 자동으로 빨라집니까? 아닙니다. GDS 는 조건이 맞아야 쓰이고 아니면 호환 모드로 내려가며, 로더 접근 패턴이 나쁘면 대역폭을 쓰지 못합니다. 벤더 벤치마크는 조건(GPU, 모델, 망)을 확인하고 제 환경에서 다시 재야 합니다.
  4. KV 캐시 오프로드가 손해가 되는 경우는? 적중률이 손익분기 아래이거나, 저장 계층 대역폭이 낮아 불러오는 시간이 재계산보다 길거나, 접두사가 짧고 매번 다른 경우입니다. 필자의 실측에서는 처음 읽기 약 4% 손해, 손익분기 적중률 약 4% 였습니다.
  5. 여러 인스턴스가 접두사를 공유하려면? 공유 L1(노드당 MP 서버)이나 공유 L2(NFS/S3), P2P RDMA 로 KV 를 공유하고, 요청이 캐시를 가진 인스턴스로 가도록 접두사 인식 라우터를 둡니다. 해시 시드를 맞추지 않으면 같은 접두사가 다른 키가 됩니다.

10. 흔한 오해

참고 자료 (실제로 열어 본 것)

확인한 버전과 날짜 (2026-10-05)

LMCache v0.5.5(2026-09-12), vLLM v0.30.0(2026-09-22, docs.vllm.ai/en/latest 는 개발판 문서), Dynamo v1.5.0, Kubernetes v1.37.1, Model Streamer 이전 공지 2026-09-13.

확인하지 못한 것. WEKA 와 DDN 공식 문서, Lustre 의 GDS 세부 조건, 컨테이너 런타임별 이미지 볼륨 지원 버전, VAST 제품의 현재 정식 명칭과 가격, MP 모드의 성능(재측정하지 않음), vLLM 안정판 문서와 개발판 문서의 차이.

로그인하면 좋아요를 누를 수 있습니다

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다