vLLM CUDA Graph와 enforce-eager, 부팅 실패와 추론 지연을 나눠 조정하는 법

GPU 서버와 추론 작업을 상징하는 그래픽카드, 냉각 장치, 데이터 케이블 1

AI를 활용하여 생성한 이미지입니다.

vLLM 서버가 처음 뜨는 데 오래 걸리거나 GPU 메모리 부족으로 부팅에 실패할 때, 요청 처리 속도까지 한 번에 낮추는 식으로 해결하면 원인을 놓치기 쉬워요. --enforce-eager는 CUDA Graph와 컴파일을 끄는 진단용 선택지에 가깝고, 운영 환경의 최종 성능 설정과는 분리해서 판단해야 합니다.[1][2]

CUDA Graph와 eager 실행의 차이

vLLM의 기본 동작에서는 CUDA Graph와 eager 실행을 섞는 하이브리드 경로를 사용해 성능과 유연성을 맞춥니다. --enforce-eager를 켜면 PyTorch eager 모드만 사용하고 CUDA Graph를 끄며, 현재 문서 기준으로 torch.compile도 함께 비활성화돼요.[1]

이 차이는 두 구간에서 나타납니다. 서버 시작 때는 그래프 캡처와 컴파일 과정이 줄어 부팅 원인을 좁히기 쉬워지고, 서버가 안정된 뒤에는 반복되는 디코드 작업에서 CUDA Graph가 주는 성능 이점을 잃을 수 있어요. 따라서 부팅 문제를 확인하려고 켠 플래그를 운영 설정으로 그대로 남기면 지연시간과 처리량이 달라질 수 있습니다.[1][2]

관찰된 문제 먼저 바꿀 값 판단할 기준
부팅 중 컴파일·그래프 캡처에서 멈춤 --enforce-eager 모델 로드와 기본 요청이 분리되어 성공하는지
부팅은 되지만 단일 요청 지연이 큼 --performance-mode interactivity 작은 동시성에서 p95 지연이 줄어드는지
동시 요청 처리량이 낮음 --performance-mode throughput 동시성·배치 크기와 tokens/s가 함께 개선되는지
CUDA Graph 없이도 부팅 실패 모델·KV 캐시·초기 여유 메모리 eager 경로에서도 발생하는지

enforce-eager를 진단 순서에 넣기

  1. 평소 실행 명령과 모델, GPU, 동시성, 컨텍스트 길이를 기록합니다.
  2. 같은 조건에서 --enforce-eager만 추가해 부팅 결과를 비교합니다.
  3. 서버가 뜨면 짧은 입력과 긴 입력을 각각 한 번씩 보내 기본 응답이 나오는지 확인합니다.
  4. eager 경로의 부팅 로그와 기본 경로의 부팅 로그에서 메모리·컴파일·그래프 캡처 단계를 나눠 봅니다.
  5. 원인이 확인되면 진단 플래그를 제거하고 운영 후보 설정을 다시 측정합니다.
vllm serve MODEL   --enforce-eager   --max-model-len 4096

vllm serve MODEL   --performance-mode interactivity

위 명령은 예시일 뿐이고 모델 이름과 메모리 관련 값은 서버 환경에 맞게 바꿔야 해요. 특히 eager로 부팅됐다는 사실만으로 CUDA Graph가 원인이었다고 단정하면 안 됩니다. 같은 GPU에서 모델 로드, KV 캐시 할당, 컨텍스트 길이만으로도 부팅 결과가 달라질 수 있습니다.

성능 모드는 요청 패턴에 맞춰요

현재 vLLM 문서의 interactivity는 작은 배치에서 요청 하나의 끝단 지연을 낮추는 방향이고, throughput은 높은 동시성에서 전체 토큰 처리량을 우선합니다. 사용자가 한 명인 개발용 서버와 여러 요청을 묶는 서비스는 같은 GPU라도 목표가 달라요.[1]

측정할 때는 평균값 하나만 보지 말고 첫 토큰까지 걸리는 시간과 생성 토큰 속도, p95 지연을 따로 기록하세요. 짧은 요청에서 빨라졌는데 긴 요청의 p95가 악화될 수 있고, 처리량이 늘었지만 첫 응답이 늦어질 수도 있습니다. 공식 문서의 모드 설명은 방향을 제시할 뿐, 특정 GPU·모델의 수치를 보장하지는 않아요.

부팅 OOM과 steady-state 성능을 분리하기

eager 모드로도 부팅이 실패한다면 CUDA Graph만의 문제는 아닙니다. 모델 가중치가 차지하는 메모리, KV 캐시 크기, 초기 여유 메모리, 동시 요청 조건을 차례로 줄여야 해요. 반대로 eager에서는 부팅되지만 기본 모드에서만 실패한다면 컴파일·그래프 캡처가 추가로 요구하는 메모리와 시작 조건을 의심할 수 있습니다.[2]

한 번에 여러 옵션을 바꾸면 해결 여부를 설명할 수 없게 됩니다. 먼저 eager 여부만 바꾸고, 다음에 컨텍스트 길이 또는 동시성 하나만 바꾸세요. 변경마다 같은 입력 세트를 사용해야 그래프 캡처 문제와 모델 자체의 지연을 구분할 수 있습니다.

측정 표를 먼저 만들어요

vLLM 옵션을 비교할 때는 “빠르다”라는 한 문장으로 결론 내리지 말고 측정 표를 남기세요. 최소한 서버 부팅 시간, 첫 토큰까지 걸린 시간, 생성 토큰 속도, 동시 요청 수, 최대 컨텍스트 길이, GPU 메모리 사용량을 같은 조건으로 기록해야 합니다. 요청 본문도 짧은 질문과 긴 문서 요약처럼 두 종류로 나누는 편이 좋아요.

실험 고정할 조건 바꿀 조건 읽을 값
A 모델·GPU·입력 기본 설정 부팅·p95·처리량
B A와 동일 --enforce-eager 컴파일·그래프 단계와 성능 차이
C A와 동일 interactivity 또는 throughput 요청 패턴별 지연·처리량

서버를 다시 시작할 때마다 GPU의 초기 여유 메모리가 달라지면 비교가 흔들릴 수 있어요. 다른 프로세스를 종료하고, 같은 모델을 같은 방식으로 로드한 뒤, 첫 요청을 워밍업으로 분리해 기록하세요. 그래야 CUDA Graph 캡처 시간이 실제 사용자 요청 지연에 섞이지 않습니다.

점검 체크리스트

  • 기본 설정과 eager 설정의 부팅 로그를 따로 보관하기
  • 같은 모델·GPU·컨텍스트 길이로 비교하기
  • 짧은 요청과 긴 요청의 p95 지연을 따로 측정하기
  • 단일 사용자와 고동시성 서버의 목표를 구분하기
  • 진단 후 --enforce-eager를 운영 설정에 남길 이유를 다시 확인하기

정리하면 --enforce-eager는 “더 빠른 설정”이 아니라 CUDA Graph와 컴파일을 제외해 부팅 경로를 단순하게 보는 스위치예요. 문제가 해결됐는지 확인한 뒤에는 원래 경로로 돌아와 지연과 처리량을 다시 측정해야 합니다.

Sources

함께 읽을 글