본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 27. 17:49

llm-d Prefix-Cache Routing이 EKS 상의 Qwen 7B 성능을 2.3배 향상시킨 방법

요약

llm-d의 Prefix-Cache Routing 기술을 통해 EKS 환경에서 Qwen 7B 모델의 추론 성능을 2.3배 향상시킨 사례를 소개합니다. 기존 라운드 로빈 방식의 한계를 극복하고 KV 캐시 지역성을 활용하여 처리량을 극대화하는 방법을 다룹니다.

핵심 포인트

  • llm-d 도입 시 출력 처리량이 2,742에서 6,423 tok/s로 대폭 증가
  • 첫 번째 토큰까지의 평균 시간(TTFT)을 19.0초에서 0.86초로 단축
  • Prefix-Cache 인식 라우팅을 통해 불필요한 프리필(prefill) 연산 방지
  • vLLM 복제본 간의 KV 캐시 지역성을 최적화하여 GPU 자원 낭비 해결

서론 (Introduction)

워크로드에 반복되는 긴 접두사 (long prefixes)가 있을 때, LLM 추론 (inference)에서 라우팅 계층 (routing layer)이 얼마나 중요한지 벤치마크해보고 싶었습니다.

설정은 의도적으로 단순하게 구성했습니다: Qwen2.5-7B-Instruct, vLLM, AWS EKS, FSx for Lustre, 그리고 8개의 g5.xlarge GPU 노드입니다. 각 노드에는 하나의 NVIDIA A10G GPU가 탑재되어 있으며 하나의 vLLM 디코드 복제본 (decode replica)을 실행했습니다. 흥미로운 점은 동일한 8개의 파드 (pods)를 대상으로 비교를 진행했다는 것입니다.

한 경로는 일반적인 Kubernetes ClusterIP Service를 사용했으며, 이는 사실상 라운드 로빈 (round-robin) 방식의 트래픽 분산을 제공합니다. 다른 경로는 정밀한 접두사 캐시 인식 엔드포인트 피커 (prefix-cache-aware endpoint picker)를 갖춘 llm-d를 사용했습니다.

결과는 상당했습니다. 동일한 하드웨어와 동일한 vLLM 파드를 사용했을 때, llm-d는 512-동시성 (512-concurrency) 벤치마크를 840.2초 대신 358.7초 만에 완료했습니다. 출력 처리량 (Output throughput)은 2,742 tok/s에서 6,423 tok/s로 증가했으며, 첫 번째 토큰까지의 평균 시간 (mean time to first token)은 19.0초에서 0.86초로 단축되었습니다.

문제 (The Problem)

vLLM에는 KV 캐시 (KV cache)가 있습니다. 만약 많은 요청이 동일한 긴 접두사를 공유한다면, 프리필 (prefill) 과정을 계속해서 다시 계산하는 대신 캐시된 접두사 블록 (prefix blocks)을 재사용하는 것이 최선의 방법입니다.

하지만 함정이 있습니다: 각 vLLM 복제본 (replica)은 자신만의 KV 캐시를 가지고 있다는 점입니다.

일반적인 라운드 로빈 라우팅을 사용하면, 반복되는 접두사를 가진 요청들이 여러 복제본으로 흩어지게 됩니다. 특정 요청이 이미 적절한 KV 블록을 보유하고 있는 다른 파드가 있음에도 불구하고, 해당 접두사를 한 번도 본 적 없는 파드에 도달할 수 있습니다. 이는 클러스터가 반복적인 프리필 작업에 GPU 시간을 낭비하고, KV 캐시를 채우며, 결국 요청을 대기열에 쌓기 시작함을 의미합니다.

llm-d는 라우팅이 접두사 캐시 지역성 (prefix-cache locality)을 인식하도록 함으로써 이 특정 문제를 해결합니다. 이번 벤치마크에서 llm-d 엔드포인트 피커는 일치하는 접두사 블록을 이미 보유하고 있을 가능성이 가장 높은 복제본으로 프롬프트 (prompts)를 라우팅했습니다.

Round-robin Grafana dashboard

아키텍처 (Architecture)

Architecture diagram

벤치마크 클러스터는 AWS EKS 상에 구축되었습니다.

주요 구성 요소는 다음과 같습니다:

  • 8 x g5.xlarge GPU 노드, 각 노드당 1 x NVIDIA A10G 24 GB 탑재.
  • 1 x m6i.4xlarge 시스템 노드 (지원 워크로드용).
  • 8 x vLLM 디코딩 (decode) 포드, 각 GPU 노드당 하나씩 배치.
  • FSx for Lustre에서 마운트된 Qwen2.5-7B-Instruct 가중치 (weights).
  • 디바이스 플러그인 (device plugin), DCGM exporter, 검증기 (validators) 및 GPU 검색을 위한 NVIDIA GPU Operator.
  • Prometheus 및 Grafana를 위한 kube-prometheus-stack.
  • 정밀한 프리픽스 캐시 라우팅 (prefix-cache routing) 기능을 갖춘 llm-d EPP 라우터.
  • 동일한 디코딩 포드를 선택하는 vllm-roundrobin이라는 이름의 베이스라인 Kubernetes 서비스.

중요한 세부 사항은 두 경로 모두 동일한 8개의 vLLM 디코딩 포드를 사용했다는 점입니다. A/B 테스트에서의 유일한 유의미한 차이점은 라우팅 계층 (routing layer)이었습니다.

Solution schema

구현 (Realization)

인프라는 Terraform으로 생성되었으며, 이후 Helm과 스크립트를 사용하여 클러스터 종속성 (dependencies)을 설치했습니다:

./scripts/tf-init.sh
./scripts/tf-apply.sh
./scripts/update-kubeconfig.sh
...

모델 가중치는 런타임 (runtime) 시점에 추론 포드 (inference pod)에 의해 다운로드되지 않았습니다. 가중치는 이미 FSx for Lustre에 준비되어 있었으며 포드에 마운트되었습니다. 이를 통해 포드 재시작 속도를 훨씬 빠르게 만들 수 있었고, 대규모 모델 다운로드가 벤치마크 경로에 영향을 주는 것을 방지했습니다.

llm-d 테스트를 위해, 정밀한 프리픽스 캐시 라우팅 스택을 설치하고 deploy/llm-d/ 하위의 리포지토리 커스터마이징 (repo customization)을 사용했습니다. 중요한 요소들은 다음과 같습니다:

  • patch-vllm.yaml: 레플리카(replica)당 1개의 GPU, 로컬 FSx Qwen 경로, GPU 스케줄링(scheduling), ZMQ를 통한 KV 이벤트, 그리고 --block-size=64를 설정했습니다.
  • router.values.yaml: EPP 라우터(router)와 정밀한 프리픽스 캐시 스코어러(prefix-cache scorer)를 설정했습니다.
  • fsx-pvc.yaml: llm-d 네임스페이스(namespace)를 위한 정적 FSx PV/PVC를 추가했습니다.
  • baseline-rr-service.yaml: 라운드 로빈(round-robin) 베이스라인을 위한 일반 Kubernetes 서비스(Service)를 생성했습니다.

또한 몇 가지 실질적인 주의 사항(gotchas)이 있었습니다:

  • llm-d 데모를 실행하는 동안 베이스라인 vLLM 배포(deployment)를 0으로 스케일링(scaled to zero)해야 했습니다. 그렇지 않으면 GPU 하나를 점유하여 여덟 번째 디코드(decode) 포드(pod)를 스케줄링할 수 없었습니다.
  • llm-d EPP 포드는 컨테이너가 요구하는 CPU와 메모리가 충분히 커서 아주 작은 시스템 노드(system node)로는 부족했기 때문에 더 큰 시스템 노드가 필요했습니다.
  • vLLM 포드에는 enableServiceLinks: false 설정이 중요했습니다. Kubernetes 서비스 환경 변수가 vLLM 자체의 VLLM_PORT와 충돌할 수 있기 때문입니다.
  • vLLM의 --block-size와 라우터 스코어러(router scorer)의 블록 크기(block size)가 일치해야 했습니다.

8 node Grafana overview

벤치마크 설정 (Benchmark Setup)

주요 벤치마크는 반복되는 프리픽스(repeated-prefix) 데이터셋과 함께 vllm bench serve를 사용했습니다:

dataset: prefix_repetition
prefixes: 150
prefix length: 2048 tokens
...

벤치마크는 2026년 6월 15일에 수집되었습니다.

또한 초당 요청 수(requests per second)를 20, 40, 60으로 설정한 더 작은 규모의 레이트 래더(rate ladder) 테스트도 수행했습니다. 이를 통해 라운드 로빈(round-robin) 경로가 포화(saturating)되기 시작하는 지점과 llm-d가 여전히 유용한 여유 공간(headroom)을 가지고 있는 지점을 확인하는 데 도움이 되었습니다.

결과 (Results)

다음은 512 동시성(concurrency) 결과입니다:

지표 (Metric)라운드 로빈 (Round-robin)llm-dllm-d 이점 (advantage)
성공 / 실패 요청 수 (Successful / failed requests)9000 / 09000 / 0동일 (Same)
...

레이트 래더(rate ladder)에서도 동일한 형태가 나타났습니다:

요청 속도 (Requested rate)엔드포인트 (Endpoint)달성된 req/s (Achieved req/s)출력 tok/s (Output tok/s)평균 TTFT (Mean TTFT)P99 TTFT평균 TPOT (Mean TPOT)
20 req/sRound-robin7.051,805.93,338.5 ms17,075.5 ms78.5 ms
...

Round-robin 방식은 매우 일찍 포화 상태에 도달했습니다. 40 또는 60 req/s를 요청했을 때조차 약 8~9 req/s만을 처리했습니다. 그 결과 TTFT는 수십 초 단위로 무너졌습니다.

물론 llm-d가 GPU를 무한히 빠르게 만든 것은 아닙니다. 8개의 A10G는 여전히 물리적인 한계를 가지고 있습니다. 하지만 llm-d는 방대한 양의 반복적인 프리필 (prefill) 작업을 피함으로써, 유효한 성능 한계치(ceiling)를 훨씬 더 높게 끌어올렸습니다.

llm-d가 승리한 이유

해당 워크로드에는 150개의 반복되는 긴 프리픽스 (prefix)가 있었습니다. 이는 캐시 지역성 (cache locality)이 매우 중요한 유형의 트래픽입니다.

Round-robin 방식은 어떤 레플리카 (replica)의 KV 캐시 (KV cache)에 어떤 프리픽스가 있는지 알지 못한 채 요청을 분산했습니다. 따라서 트래픽이 다르게 라우팅되었다면 수행할 필요가 없었을 작업임에도 불구하고, 요청들이 계속해서 레플리카에 프리필 (prefill) 작업을 강제했습니다.

llm-d를 사용하면 vLLM이 KV 이벤트를 방출하고, 라우터는 이 이벤트들을 사용하여 레플리카들에 대한 프리픽스 캐시 인식형 (prefix-cache-aware) 뷰를 구축합니다. 다음 요청이 도착하면, 엔드포인트 선택기 (endpoint picker)는 관련 프리픽스 블록을 이미 보유하고 있는 레플리카를 우선적으로 선택할 수 있습니다.

결과:

  • 프리픽스 캐시 적중률 (Prefix cache hit rate)이 약 11%에서 약 93%로 증가했습니다.
  • 대기 중인 요청 (Waiting requests)이 약 180개에서 0개로 감소했습니다.
  • KV 캐시 (KV cache) 사용률이 99% 근처에 고정되는 대신 약 64-71% 수준을 유지했습니다.
  • 출력 처리량 (Output throughput)이 두 배 이상 증가했습니다.
  • 평균 TTFT가 약 95% 감소했습니다.

가장 흥미로운 점은 이것이 모델 변경, GPU 변경, 또는 레플리카 수의 변경이 아니었다는 것입니다. 바로 라우팅 계층 (routing layer)의 변화였습니다.

NVIDIA SMI during load

실행 노트

vLLM 로그를 보면 프리픽스 적중률 (prefix hit rate)이 예열되는 동안 llm-d 경로가 대기 큐 (waiting queue) 없이 실행되는 것을 확인할 수 있습니다:

실행 중: 64 reqs, 대기 중: 0 reqs, GPU KV cache 사용률: 62.1%, Prefix cache hit rate: 63.5%
실행 중: 68 reqs, 대기 중: 0 reqs, GPU KV cache 사용률: 68.9%, Prefix cache hit rate: 66.4%
실행 중: 76 reqs, 대기 중: 0 reqs, GPU KV cache 사용률: 70.1%, Prefix cache hit rate: 72.7%

최종 집계 결과는 정상 상태 (steady-state)의 Grafana 뷰보다 더 넓은 P99 TTFT (Time To First Token)를 보여주었는데, 이는 실행 시작 단계에 콜드 캐시 (cold-cache) 램프업 (ramp-up) 과정이 포함되었기 때문입니다. 캐시가 예열된 후, TTFT 중앙값 (median)은 340 ms였으며, 정상 상태 대시보드는 시스템이 큐 축적 없이 512-동시성 (concurrency) 트래픽을 처리하는 것을 보여주었습니다.

또한 DescribeFileSystems 권한 누락에 대한 FSx CSI 컨트롤러 경고가 있었습니다. 이 설정에서는 정적 FSx PV/PVC 구성을 사용했기 때문에 이것이 차단 요인이 되지는 않았습니다. 파일 시스템 ID와 마운트 세부 정보가 이미 알려져 있었으므로, 동적 FSx 검색 (discovery)은 벤치마크 경로의 일부가 아니었습니다.

결론

이번 벤치마크는 LLM 추론 성능이 단순히 GPU 개수에만 달려 있는 것이 아니라는 점을 잘 상기시켜 주었습니다.

반복되는 접두사 (repeated-prefix) 워크로드의 경우, 라우팅 계층 (routing layer)은 클러스터가 KV cache를 재사용할지, 아니면 동일한 긴 접두사 (long prefixes)를 계속해서 다시 계산할지를 결정할 수 있습니다. 이번 실행에서 llm-d의 정밀한 Prefix-cache 라우팅은 동일한 8 x A10G 플릿 (fleet)이 워크로드를 2.3배 더 빠르게 완료하도록 만들었으며, 동시에 평균 TTFT를 95% 절감했습니다.

만약 귀하의 트래픽에 공유된 시스템 프롬프트 (system prompts), 긴 공통 지침 (long common instructions), 검색 템플릿 (retrieval templates), 채팅 접두사 (chat prefixes) 또는 에이전트 스캐폴딩 (agent scaffolding)이 포함되어 있다면, 라운드 로빈 (round-robin) 라우팅은 조용히 많은 GPU 시간을 낭비할 수 있습니다. Prefix-cache-aware 라우팅은 아키텍처 다이어그램에서는 작아 보이지만 벤치마크 결과에서는 매우 크게 나타나는 변화 중 하나입니다.

이 기사가 즐거우셨기를 바랍니다.

제 모든 코드는 제 GitHub 저장소에서 확인하실 수 있습니다: https://github.com/andygolubev/aws-eks-inference-llmd-vllm-benchmark-qwen-7b

LinkedIn에서 저와 자유롭게 연결해 주세요: https://www.linkedin.com/in/andy-golubev/

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0