26S24c

QK·2026년 9월 25일

1. 공유해주신 문서의 기술적 오류

문서는 MemKV를 Minio() 클라이언트로 get_object()를 호출하는 일반 S3 오브젝트 스토리지처럼 다루고 있는데, 실제 MemKV는 그렇게 동작하지 않습니다.

  • MemKV는 HTTP/S3 프로토콜을 의도적으로 우회하고, NVMe↔GPU 사이를 RDMA로 직결하는 "G3.5 메모리 티어"입니다. 애플리케이션 코드에서 get_object로 부르는 범용 캐시가 아니라, 추론 엔진(vLLM 등)의 KV 캐시 관리 계층에 직접 연결되는 인프라입니다.
  • NVIDIA BlueField-4 STX DPU와 NVIDIA Dynamo/NIXL과의 통합을 전제로 설계됐습니다(공식 발표 기준 2026년 5월).
  • 즉, RAG 청크나 세션 히스토리를 "저장하는 대상"이 아니라, 모델이 이미 계산한 KV 캐시(어텐션 텐서)가 HBM에서 밀려날 때(eviction) 재계산 대신 복원해오는 대상입니다.

문서의 시나리오 1(RAG 청크 인출), 2(시맨틱 캐시), 3(세션 상태)은 전부 "빠른 스토리지 vs 느린 스토리지" 일반 테스트이지, MemKV 고유의 메커니즘(KV 캐시 티어링)을 검증하는 게 아닙니다. 이 코드 그대로 벤치마크를 돌리면 "MemKV가 빠른 NVMe 오브젝트 스토리지구나"까지는 증명해도, MemKV를 도입해야 할 진짜 이유(재계산 세금 제거)는 증명하지 못합니다.

2. 그럼 진짜 테스트는 어떻게 해야 하나

방금 확인해보니, vLLM은 v0.22부터 네이티브 Tiered KV Cache Offloading을 지원합니다(--kv-transfer-config). 여기 2차 티어로 로컬 파일시스템(NVMe), NIXL 경유 오브젝트 스토리지(vLLM 공식 블로그 예제가 MinIO 엔드포인트를 직접 씀), P2P(RDMA)를 붙일 수 있습니다. 이게 MemKV를 실제로 검증하는 올바른 연결점입니다 — 애플리케이션 코드가 아니라 vLLM 서버 기동 옵션 레벨에서 붙습니다.

이 구조를 이용해서 시나리오를 다시 설계하면:

시나리오 A. KV 캐시 오프로드 티어 비교 (문서의 시나리오1을 올바르게 재설계)

  • Baseline: 2차 티어 없음 (HBM 가득 차면 무조건 재계산)
  • 대조군: 로컬 NVMe fs 티어 ({"type": "fs", "root_dir": "/mnt/local-nvme-cache/kv-cache"}) — 지금 B300 노드만으로 바로 테스트 가능
  • 실험군: MemKV 티어
  • 부하: stage3a9(8k~64k 장문) + stage3a6(고동시성)을 동시에 걸어서 실제로 HBM KV 캐시가 꽉 차 eviction이 발생하는 조건을 인위적으로 만들어야 함 (지금까지처럼 저동시성 단독 테스트로는 eviction 자체가 안 일어나서 차이가 안 보입니다)
  • 측정: eviction 후 재요청 시 TTFT, GPU idle 비율

시나리오 B. 레플리카 간 KV 캐시 공유 (문서에 없던, MemKV만의 진짜 차별점)

  • stage3a2/stage3a7에서 이미 만든 TP4x2(레플리카 2개) 구성을 활용
  • 레플리카 A가 처리한 프리픽스를 레플리카 B가 MemKV의 공유 풀을 통해 재사용 가능한지 확인 — 로컬 NVMe로는 원천적으로 불가능한 시나리오라서 이게 MemKV 도입 여부를 가르는 가장 강력한 근거가 될 수 있습니다.

시나리오 C. 장기 멀티턴 세션 (문서의 시나리오3을 올바르게 재설계)

  • 문서처럼 별도 상태 저장소에 읽고 쓰는 게 아니라, KV 캐시 자체가 턴이 늘어날수록 커지다가 HBM에서 밀려나는 상황을 재현 — 밀려난 턴을 다시 참조할 때 MemKV 복원 vs 전체 재계산 비교

3. 바로 진행하기 전에 확인이 필요한 것

  • MemKV가 vLLM의 secondary_tiers에 어떤 type으로 붙는지 확정 안 됨 — 일반 "obj"(NIXL 경유 S3 호환)로 붙는지, 별도 전용 커넥터가 필요한지는 docs.min.io/memkv/의 vLLM 연동 가이드로 확인이 필요합니다. 여기서 틀린 설정으로 스크립트를 만들면 테스트 자체가 의미 없어집니다.
  • 하드웨어 전제 조건: MemKV는 BlueField-4 STX DPU + RDMA 패브릭을 전제로 설계됐습니다. 지금까지의 B300 bare-metal 단일 노드 구성에 이 하드웨어가 실제로 있는지부터 확인이 필요합니다.

제안

지금 바로 만들어드릴 수 있는 건 시나리오 A의 절반(Baseline vs 로컬 NVMe fs 티어) — 이건 하드웨어 추가 없이 stage3a9+stage3a6 부하를 결합해서 바로 검증 가능하고, MemKV 붙였을 때와 비교할 "기준선"으로도 그대로 쓸 수 있습니다. MemKV 실제 연동 부분(type 확정, DPU 유무)은 확인되는 대로 이어서 만들어드리면 될 것 같은데, 어떻게 진행할까요?

==

RDMA 없이 TCP/IP 기준으로 다시 설계하겠습니다. 핵심 변화는 이렇습니다: NIXL은 UCX 트랜스포트를 쓰는데 RDMA 하드웨어가 없으면 TCP로 자동 폴백되는 경로가 있어서(LMCache 문서에도 "NVLink, RDMA, or TCP" 언급), 연결 자체는 됩니다 — 다만 MemKV가 마케팅하는 마이크로초 단위 RDMA 성능은 기대하면 안 되고, "TCP 경유로도 재계산 회피 효과가 남는지"가 이번 테스트의 진짜 질문이 됩니다. 이 전제를 스크립트 전체에 명시하겠습니다.

또한 실제로 KV 캐시가 밀려나는(eviction) 상황을 인위적으로 만들어야 차이가 보이므로, vllm bench serve의 랜덤 데이터셋 대신 재사용 가능한 고정 문서 풀 + Zipfian 분포로 부하를 주는 전용 클라이언트를 만들겠습니다.Zipfian 가중치(상위 20% 문서 = 트래픽 80%)와 문서 생성 로직 정상 동작 확인했습니다. README/사용 설명서에 반영하고 전달드리겠습니다.TCP/IP 전제로 2개 파일을 만들었습니다.

  • stage3a10_kv_tier_client.py: 재사용 가능한 고정 문서 풀(RAG형, Zipfian 분포로 상위 20%가 트래픽 80%) + 멀티턴 세션(컨텍스트 계속 누적) 두 패턴을 지원하는 비동기 부하 클라이언트. 스트리밍 응답 첫 청크 도착 시각으로 TTFT를 근사 측정합니다.
  • stage3a10_vllm_kv_tier_comparison.sh: vLLM의 --kv-transfer-config로 Baseline(티어 없음) → 로컬 NVMe fs 티어 → MemKV(obj, TCP) 세 조건을 순서대로 띄우고 동일 부하로 비교합니다. GPU_MEM_UTIL_FOR_EVICTION을 의도적으로 낮춰(기본 0.55) eviction이 실제로 일어나게 유도했습니다.

핵심 설계 포인트:

  • MemKV 조건은 MEMKV_ENDPOINT가 없으면 자동 스킵 — 지금 당장은 baseline vs 로컬 NVMe(fs)만 비교해도 "티어링 자체의 효과"는 검증 가능하고, MemKV 엔드포인트가 준비되면 같은 스크립트에 환경변수만 채워서 이어서 돌리면 됩니다.
  • 해석 기준을 TCP 전제로 명시: fs_tier가 baseline보다 나으면 "티어링 자체는 유효", memkv_tier가 fs_tier보다도 나으면 "RDMA 없이도 MemKV 순증 가치 있음", 비슷하거나 못하면 "이 환경에서는 MemKV 이점이 제한적 — RDMA 확보 전까지 우선순위 낮게" 라는 판단 근거를 자동으로 리포트에 남기게 했습니다.
  • MemKV의 실제 secondary_tiers 스키마(type: obj 여부)는 여전히 docs.min.io/memkv/ 재확인이 필요하다는 주석을 스크립트 상단에 남겨뒀습니다 — 여기가 틀리면 조건3 서버 기동 자체가 실패할 것이고, 그 경우 skipped.txt와 server_memkv_tier.log로 원인 확인하시면 됩니다.
  • P2P(레플리카 간 KV 공유) 시나리오는 RDMA 의존도가 너무 커서 이번 라운드에서는 의도적으로 제외했습니다 — 나중에 RDMA 확보되면 알려주세요.

===

이번 의견은 3a 스크립트 몇 개를 고치는 수준을 넘어서, 테스트를 어느 단계에 배치할지 자체를 재검토하게 만드는 내용이 꽤 있습니다. 전체 구조 관점에서 정리하겠습니다.

1. 구조적으로 가장 중요한 지적: "Phase 0(MemKV 자체 성능)"은 3a가 아니라 2단계 소관입니다

의견의 Phase 0(블록 크기 2MB~64MB 스윕, READ/WRITE GB/s, IOPS, RDMA/NVMe 활용률)은 "MemKV 자체가 빠른가"를 재는 순수 인프라 벤치마크입니다. 이건 지금까지 우리가 쌓아온 구조와 정확히 같은 성격입니다 — 2단계(네트워크/스토리지 HW 벤치마크)에서 MinIO AIStor를 warp로 재던 것과 동일한 층위이지, 3a(추론 애플리케이션 레벨)의 일이 아닙니다.

→ 반영 필요: MemKV 자체 wire 벤치마크는 stage2 계열에 새 스크립트(예: stage2c_memkv_wire_benchmark.sh)로 넣는 게 맞습니다. 3a는 "MemKV가 빠른 것을 전제로, 그게 vLLM 추론에 실제로 이득이 되는가"만 다뤄야 이미 만든 단계 구분 철학과 일관됩니다.

2. "Capacity Cliff"(Test D, Graph 6) = 이미 만든 stage3a6의 재활용입니다

의견의 가장 강력한 그림으로 꼽은 "동시성을 늘렸을 때 baseline은 무너지고 MemKV는 절벽을 뒤로 미룬다"는 개념은, stage3a6_vllm_saturation_test.sh가 하는 일과 구조적으로 동일합니다(concurrency를 올려가며 SLA 붕괴 지점을 찾는 것).

→ 반영 필요: 새 스크립트를 또 만들기보다, stage3a6에 --kv-tier {none|fs|memkv} 파라미터를 추가해서 같은 포화점 테스트를 티어 조건별로 반복 실행 → 포화점이 얼마나 뒤로 밀리는지를 자동 비교하는 게 맞습니다. 이렇게 해야 지난번 만든 stage3a_summary_report.py가 이 결과도 자동으로 같이 집계할 수 있고, 도구가 중복되지 않습니다.

3. 새로 드러난 환경 정보 — K8s/Cilium/BGP는 5단계(연동)에 큰 영향

의견 말미에 "B300 8-GPU + RHEL10.2 + K8s/Cilium/BGP 환경"이 언급됐는데, 이건 지금까지 저와 논의한 bare-metal 범위 밖의 정보입니다. 지금 미룬 5단계(클러스터 연동)를 실제로 진행할 때, "RDMA 경로가 Cilium CNI + BGP 오버레이를 통과해서 실제로 형성되는지" 자체가 별도 검증 항목이 되어야 합니다. bare-metal에서 RDMA가 잘 되더라도 K8s 오버레이 네트워크에서는 RDMA/RoCE가 막히거나 성능이 크게 떨어지는 경우가 흔합니다.

→ 반영 필요: 지금 당장은 아니지만, 5단계 계획에 "K8s(Cilium/BGP) 환경에서 RDMA 패스스루 검증"을 명시적으로 메모해두시길 권합니다 — 나중에 잊혀지기 쉬운 항목입니다.

4. KPI 프레이밍 원칙은 MemKV뿐 아니라 전체 보고서 철학에 적용할 만합니다

의견 19번의 지적("Fetch latency가 아니라 GPU compute eliminated/capacity 증가로 말해야 한다")은 사실 MemKV에만 국한되지 않는, 이 프로젝트 전체 최종 보고서의 일반 원칙입니다. 지금까지 만든 03_PASS_CRITERIA_stage1_stage3.md나 stage3a_summary_report.py도 "컴포넌트가 빠르다"보다 "GPU 인프라의 실효 용량/비용이 어떻게 바뀌는가"로 결론을 내는 톤을 유지하는 게 낫겠습니다.

5. 이번 의견이 지적한, 지금 stage3a10 스크립트의 구체적 보완점

  • 제가 썼던 docs.min.io/memkv/ 대신, 의견이 정확히 짚어준 docs.min.io/memkv/integrate/vllm-offloading/(vLLM 네이티브 오프로딩 공식 연동 가이드)로 스크립트 주석의 참조 링크를 교체해야 합니다.
  • MemKV는 일반 vLLM tiered-offload 경로 외에 "direct" GPU KV tensor → RDMA → MemKV 경로도 별도로 제공하는데, 이건 RDMA 필수라 지금 TCP-only 환경에서는 애초에 테스트 대상이 아닙니다. 지금 stage3a10이 쓰는 건 전자(tiered-offload "obj" 타입)뿐이라는 걸 스크립트 주석에 명확히 남겨야 오해가 없습니다.

변경 없이 그대로 유지해도 되는 부분

  • 1단계(HW)와 4단계(학습)는 이 의견과 무관합니다 — MemKV는 순수 추론 서빙/스토리지 주제라, 다른 단계에 손댈 필요 없습니다.
  • Semantic Cache(의견의 Test 4)는 "MemKV의 KV 캐시 벤치마크와는 다른 애플리케이션 레벨 문제"라는 의견의 판단은 지난번 제 판단과 동일해서, 이 부분은 추가 반영 불필요합니다.

정리하면, 3a 스크립트 자체보다 "어디에 뭘 배치할지"가 더 중요한 반영 사항입니다. 원하시면 ① stage2c_memkv_wire_benchmark.sh 신규 작성, ② stage3a6에 --kv-tier 옵션 추가, ③ stage3a10 주석/링크 보정을 지금 진행해드릴까요?

==

세 가지 순서대로 반영하겠습니다.이제 ② stage3a6에 KV 티어 비교를 추가하겠습니다 — capacity cliff가 티어별로 얼마나 밀리는지 자동 비교되도록 구조를 바꿉니다.stage3a6의 출력 구조가 바뀌었으니, 이전에 만든 stage3a_summary_report.py의 3a6 파싱 로직도 같이 맞춰야 합니다. 업데이트하겠습니다.정상 동작 확인했습니다. 이제 ③ stage3a10 주석/링크를 보정합니다.README와 사용 설명서를 최종 반영합니다.이제 사용 설명서에도 반영합니다.세 가지 전부 반영했습니다.

  • ① stage2c_memkv_wire_client.py + stage2c_memkv_wire_benchmark.sh: 2단계 소관으로 신규 배치. 블록 크기(2~64MB) × 동시성(1~512) 스윕, PUT/GET 지연 P50/P95/P99, 합산 GB/s, IOPS 측정. TCP 전제이므로 MemKV 공식 RDMA 벤치마크(97.4GB/s)와 대조해 "TCP로 인한 손실폭"을 가늠하도록 해석 가이드를 넣었고, NVMe/RDMA 활용률은 MemKV 노드 자체에서만 정확히 잴 수 있다는 측정 범위 한계도 명시했습니다.

  • ② stage3a6 KV 티어 비교: none/fs/memkv 세 조건(환경변수로 자동 결정, KV_TIERS로 강제 지정 가능)에 대해 포화점 탐색을 반복 실행하도록 구조를 바꿨습니다. 각 티어는 tier_<이름>/ 하위 디렉터리에 결과가 쌓이고, 마지막에 capacity_cliff_comparison.md로 "baseline 대비 포화점이 몇 % 밀렸는지"를 자동 비교합니다 — 리뷰에서 지적한 Graph 6(capacity cliff)에 정확히 대응됩니다. 출력 구조가 바뀐 만큼 stage3a_summary_report.py의 3a6 파싱 로직도 같이 고쳐서 구버전 형식도 폴백으로 지원하게 했고, 가짜 데이터로 재검증 완료했습니다.

  • ③ stage3a10 주석/링크 보정: docs.min.io/memkv/integrate/vllm-offloading/로 정확한 링크 교체, 그리고 이 스크립트는 tiered-offload("obj") 경로만 다루고 RDMA 필수인 "direct" 경로는 범위 밖이라는 점, stage2c를 먼저 돌려서 기준선을 확보하라는 권고를 명시적으로 추가했습니다.

README와 사용 설명서(3.5절 신규, 4.3-1절 갱신)에도 실행 순서와 관계를 반영해뒀습니다. 다만 구조적으로 언급드렸던 나머지 항목(K8s/Cilium/BGP 환경에서의 RDMA 패스스루 검증은 5단계 메모, KPI 프레이밍 원칙)은 이번엔 스크립트 반영 요청 범위 밖이라 손대지 않았습니다 — 필요하시면 말씀해주세요.

profile
engineer

0개의 댓글