[faster-qwen3-tts] 생성 요청 직렬화 및 모델 캐싱 도입
PR 링크: andimarafioti/faster-qwen3-tts#27 상태: Merged | 변경: +296 / -237
들어가며
faster-qwen3-tts의 데모 서버에서 두 가지 문제가 있었다: (1) 동시 생성 요청이 CUDA graph의 static buffer를 공유하여 출력이 corrupted되고, (2) 모델을 전환할 때마다(0.6B ↔ 1.7B) 처음부터 다시 로드하여 30초 이상 대기가 필요했다.
핵심 코드 분석
생성 직렬화
_generation_lock = asyncio.Lock()
_generation_waiters: int = 0 # 대기 중인 요청 수
async def generate_stream(text, ...):
global _generation_waiters
_generation_waiters += 1
try:
async with _generation_lock:
# CUDA graph은 static buffer를 사용하므로
# 한 번에 하나의 생성만 가능
...
finally:
_generation_waiters -= 1
모델 캐싱
Before:
# 모델 로드 시 기존 모델 삭제 후 새로 로드
model = FasterQwen3TTS.from_pretrained(model_name, ...)
After:
_model_cache: dict[str, FasterQwen3TTS] = {}
async def load_model(model_name, ...):
if model_name in _model_cache:
# 캐시에서 즉시 반환
_active_model_name = model_name
return
model = FasterQwen3TTS.from_pretrained(model_name, ...)
_model_cache[model_name] = model
Preset voice 캐시 사전 로딩
def _prime_preset_voice_cache(model: FasterQwen3TTS) -> None:
"""모델 로드 시 preset voice의 voice_clone_prompt를 미리 계산"""
for preset in PRESET_VOICES.values():
model._prepare_generation(
text="warmup",
ref_audio=preset["ref_audio"],
ref_text=preset["ref_text"],
...
)
왜 이게 좋은가
- 데이터 무결성: CUDA graph의 static buffer는 동시 접근 시 race condition이 발생한다. asyncio Lock으로 이를 원천 차단한다.
- UX 개선: 모델 캐싱으로 0.6B → 1.7B → 0.6B 전환 시 두 번째부터는 즉시 로드된다.
- 대기열 가시성:
_generation_waiters카운터로 현재 대기 중인 요청 수를 UI에서 표시할 수 있다.
정리
CUDA graph 기반 서버에서 동시성 제어는 선택이 아닌 필수다. Static buffer의 in-place 업데이트 특성을 이해하고 생성을 직렬화하며, 모델 캐싱으로 전환 비용을 줄인 실용적인 PR이다.
참고 자료
이 글은 AI(Claude)의 도움을 받아 작성되었습니다. 코드 분석과 해석에서 오류가 있을 수 있으니, 정확한 내용은 원본 PR을 참고해주세요.
관련 포스트
PR Analysis 의 다른글
- 이전글 [triton] Proton 커널 런처에 더 많은 메타데이터 전달
- 현재글 : [faster-qwen3-tts] 생성 요청 직렬화 및 모델 캐싱 도입
- 다음글 [Open WebUI] ResponseMessage에서 JSON.stringify 비교를 O(1) fast-path로 우회
댓글