Boosting multimodal inference performance by >10% with a single Python dictionary
🖼️ 인포그래픽
🖼️ 4컷 인포그래픽
📰 Boosting multimodal inference performance by >10% with a single Python dictionary | Modal Blog
💡 한 줄 요약
Modal은 SGLang의 멀티모달 추론 스케줄러에서 반복적인 CUDA IPC 핸들 열기 비용을 Python dict 캐시로 제거해 Qwen2.5-VL-3B-Instruct 단일 H100 벤치마크에서 처리량 16.2%, 평균 지연 10% 이상 개선했다고 설명한다.
📌 핵심 요약
- 멀티모달 VLM은 문서 파싱과 앱을 볼 수 있는 코딩 에이전트 등에서 활용되지만, SGLang·vLLM 같은 오픈소스 추론 엔진은 새 입력 유형에 맞춘 최적화 과제가 남아 있다.
- Modal은 고객 작업 중 Qwen2.5-VL-3B-Instruct를 H100에서 벤치마킹하다가 GPU 여력보다 낮은 처리량 정체를 발견했고, GPU를 막지 말라는 추론 성능 원칙에 따라 호스트 측 오버헤드를 먼저 조사했다.
- py-spy로 SGLang 스케줄러를 프로파일링한 결과, 멀티모달 입력 처리 함수 process_input_requests와 그 안의 hash_feature 경로에서 CUDA 공유 스토리지를 반복적으로 여는 비용이 두드러졌다.
- 문제의 핵심은 스케줄러가 같은 GPU 메모리 풀 핸들을 텐서마다 반복해서 _new_shared_cuda로 다시 열고 있었다는 점이며, 이를 풀 핸들 단위로 한 번만 열어 Python dict에 캐시하도록 바꿨다.
- 캐시 적용 후 핫스팟이 사라지고 입력 처리 샘플 수가 절반 이하로 줄었으며, 단일 H100 벤치마크에서 처리량은 22.2req/s에서 25.7req/s로 16.2% 증가하고 평균 E2E 지연은 1979ms에서 1768ms로 10.6% 감소했다.
🧩 주요 포인트
- 멀티모달 VLM은 문서 파싱과 앱을 볼 수 있는 코딩 에이전트 등에서 활용되지만, SGLang·vLLM 같은 오픈소스 추론 엔진은 새 입력 유형에 맞춘 최적화 과제가 남아 있다.
- Modal은 고객 작업 중 Qwen2.5-VL-3B-Instruct를 H100에서 벤치마킹하다가 GPU 여력보다 낮은 처리량 정체를 발견했고, GPU를 막지 말라는 추론 성능 원칙에 따라 호스트 측 오버헤드를 먼저 조사했다.
- py-spy로 SGLang 스케줄러를 프로파일링한 결과, 멀티모달 입력 처리 함수 process_input_requests와 그 안의 hash_feature 경로에서 CUDA 공유 스토리지를 반복적으로 여는 비용이 두드러졌다.
- 문제의 핵심은 스케줄러가 같은 GPU 메모리 풀 핸들을 텐서마다 반복해서 _new_shared_cuda로 다시 열고 있었다는 점이며, 이를 풀 핸들 단위로 한 번만 열어 Python dict에 캐시하도록 바꿨다.
- 캐시 적용 후 핫스팟이 사라지고 입력 처리 샘플 수가 절반 이하로 줄었으며, 단일 H100 벤치마크에서 처리량은 22.2req/s에서 25.7req/s로 16.2% 증가하고 평균 E2E 지연은 1979ms에서 1768ms로 10.6% 감소했다.
🧠 상세 정리
1. 멀티모달 추론 엔진의 성능 병목 문제
글은 멀티모달 비전-언어 모델이 인공지능에 ‘눈’을 제공한다는 설명에서 출발한다. 사용자는 작은 VLM을 비정형 문서 파싱에 쓰고, 더 큰 모델은 자신이 설계하는 앱을 볼 수 있는 멀티모달 코딩 에이전트에 활용한다. 하지만 이러한 새로운 입력 유형과 모델은 SGLang과 vLLM 같은 오픈소스 추론 엔진에 새로운 부담을 만든다. 저자는 성능 극대화가 한 번의 거대한 변화가 아니라 작은 개선을 집요하게 쌓아 해결되는 문제라고 전제한다. 이번 사례는 그중 하나로, 단순한 Python dictionary 기반 캐시가 실제 추론 처리량과 지연 시간에 의미 있는 영향을 준 과정을 다룬다.
2. 고객 벤치마크에서 발견된 처리량 정체
Modal 팀은 고객과 함께 Qwen2.5-VL-3B-Instruct를 H100에서 벤치마킹하던 중 SGLang의 처리량이 GPU가 감당할 수 있는 수준보다 낮은 지점에서 정체되는 현상을 발견했다. 이때 저자는 추론 성능 엔지니어링의 기본 원칙으로 ‘GPU를 절대 막지 말라’를 강조한다. 성능 문제가 보이면 곧바로 CUDA 커널이나 Nsight Compute의 세부 stall 원인을 파고들기보다, 먼저 호스트에서 무슨 일이 벌어지는지 확인해야 한다는 것이다. SGLang 같은 추론 엔진에서 스케줄러는 GPU 작업 제출을 관문처럼 제어하는 단일 스레드 루프다. 따라서 스케줄러에서 소비되는 1밀리초는 진행 중인 모든 요청의 prefill과 decode 반복을 지연시키는 비용이 된다.
3. py-spy로 드러난 process_input_requests 병목
팀은 실행 중인 SGLang 스케줄러 프로세스에 Python 프로파일링 도구 py-spy를 붙여 30초 동안 멀티모달 트래픽을 샘플링했다. 약 3천 개의 샘플과 flamegraph를 얻은 결과, process_input_requests라는 함수가 스케줄러 전체 CPU 시간의 약 13%를 소비하며 눈에 띄는 병목 후보로 나타났다. 이 함수는 들어오는 멀티모달 요청을 준비해 스케줄러가 배치를 만들고 GPU로 보낼 수 있게 하는 역할을 한다. 더 깊이 들어가 보니 대부분의 시간이 hash_feature 함수에 쓰이고 있었다. 이 함수는 입력 이미지를 해시 기반 ID로 매핑해 KV 캐시 조회 중 저렴하게 식별할 수 있게 하는데, 텍스트 토큰과 달리 이미지 입력에는 자연스러운 vocabulary index가 없기 때문에 새로 생긴 멀티모달 코드 경로였다.
4. 반복적인 CUDA 공유 스토리지 열기가 만든 호스트 오버헤드
hash_feature 내부에서 특히 의심스러운 부분은 reconstruct_on_target_device 호출과 그 아래의 torch.UntypedStorage._new_shared_cuda 호출이었다. 해당 호출은 hash_feature 시간의 약 25%, 전체 스케줄러 런타임의 3% 조금 넘는 비중을 차지했다. SGLang은 스케줄러와 tokenizer worker가 서로 다른 프로세스에서 동작하며, 큰 on-device 텐서를 복사 없이 넘기기 위해 CUDA Interprocess Communication을 사용한다. 각 worker는 자체 GPU 메모리 풀을 갖고, 텐서는 그 풀의 slice로 표현되며, 풀 핸들과 offset 메타데이터로 식별된다. 문제는 스케줄러가 이 메모리 풀 핸들을 텐서마다 _new_shared_cuda로 반복해서 열고, 스케줄러 반복이 끝나면 그 장부를 버리는 방식으로 동작했다는 점이다.
5. Python dict로 구현한 CUDA IPC Pool Handle Cache
저자들은 같은 소수의 핸들을 계속 다시 여는 대신, 풀마다 한 번만 열고 재사용하면 된다고 판단했다. CUDA API 수준에서는 반복 open이 reference-counted라 비교적 싸지만, 실제 오버헤드는 PyTorch wrapper 쪽의 새 StorageImpl 생성, CUDA event recording, GIL 상호작용, allocator 장부 처리에서 발생한다고 설명한다. 해결책은 거창한 이름의 ‘CUDA IPC Pool Handle Cache’지만 구현은 단순한 Python dict다. 캐시 키는 pool_device_index와 pool_handle의 tuple이며, 저장된 storage가 없을 때만 lock을 잡고 _new_shared_cuda를 호출해 캐시에 넣는다. 풀은 재할당되지 않기 때문에 캐시 무효화가 필요 없고, 쓰기는 매우 드물어 읽기 경로에서는 lock이 필요하지 않다는 점도 성능상 유리하다.
6. 엔드투엔드 성능 개선과 배포 상태
수정 후 같은 부하에서 py-spy 프로파일링을 다시 수행하자 _new_shared_cuda 핫스팟이 사라졌고, 입력 처리 관련 샘플 수도 절반 이하로 줄었다. 그러나 저자는 flamegraph 개선만으로 실제 사용자 관점의 성능 향상을 보장할 수 없기 때문에 다시 Qwen2.5-VL-3B-Instruct 단일 H100 벤치마크를 수행했다. 결과적으로 request throughput은 22.2req/s에서 25.7req/s로 16.2% 개선됐고, TTFT 평균은 965ms에서 838ms로 13.2% 감소했으며, TPOT 평균은 72ms에서 60ms로 17.2% 줄었다. 평균 E2E latency도 1979ms에서 1768ms로 10.6% 낮아졌고 p99 지연도 개선됐다. 이 최적화는 SGLang PR로 upstream됐으며 v0.5.10 릴리스에 포함되어, CUDA IPC 사용 시 SGLANG_USE_IPC_POOL_HANDLE_CACHE=1 플래그로 활성화할 수 있다.
🧾 핵심 주장 / 시사점
- 멀티모달 추론 성능 문제는 GPU 커널 자체보다 스케줄러 같은 호스트 측 단일 스레드 경로에서 먼저 드러날 수 있으며, 작은 book-keeping 비용도 모든 요청의 prefill·decode를 지연시킬 수 있다.
- 이번 개선의 핵심은 복잡한 알고리즘 변경이 아니라 반복적으로 재생성하던 공유 메모리 핸들 상태를 한 번 열어 재사용한 점이며, hot path에서 불필요한 객체 생성과 동기화 비용을 제거한 사례다.
- 입력/prefill 경로의 최적화가 decode latency까지 개선된 것은 SGLang 스케줄러가 요청 처리, 배치 형성, GPU dispatch를 단일 스레드에서 수행하기 때문에 한 구간의 지연이 전체 파이프라인으로 전파된다는 점을 보여준다.
✅ 액션 아이템
- 원문에서 강조한 핵심 변화와 이해관계자를 기준으로 Boosting multimodal inference performance by >10% with a single Python dictionary | Modal Blog의 영향을 정리한다.
- 다음 의사결정이나 제품/정책 판단에 연결될 수 있는 근거를 원문 문장과 함께 기록한다.
- 기사에서 제시한 수치·사례·제약 조건을 분리해 과장 없이 검토한다.
- 후속 모니터링이 필요한 발표·제품·정책 변화가 있는지 출처 링크를 기준으로 추적한다.
❓ 열린 질문
- Introducing Notebooks – launch ML experiments with zero cold boots]]" "207. 이 변화가 실제 사용자나 조직의 선택 기준을 어떻게 바꿀까?
- Run GPU jobs from Airflow with Modal" "240. 이 근거가 다른 산업이나 지역에서도 동일하게 적용될 수 있을까?
- Nemotron 3.5 Content Safety Customizable Multimodal Safety for Global Enterprise AI" "210. 기사에서 아직 검증되지 않은 전제나 리스크는 무엇일까?
- AI Doesn’t Live in Text Alone" "[[25. 후속 발표나 데이터가 나오면 어떤 지표를 먼저 비교해야 할까?
