본문으로 건너뛰기

© 2026 Molayo

HuggingFace헤드라인2026. 05. 07. 13:44

효율적인 요청 큐잉 - LLM 성능 최적화

요약

본 기사는 LLM 추론 엔진(vLLM 등)의 성능 최적화와 공정한 사용자 경험 제공에 필요한 고급 스케줄링 전략을 다룹니다. GPU 효율성을 위해 요청을 배치 처리하는 백엔드 큐가 필수적이지만, 이로 인해 특정 사용자가 과도하게 많은 요청을 보내면 다른 사용자들이 불필요하게 지연되는 문제가 발생합니다. 따라서 LLM-Server와 같은 프론트 스케줄러 계층에서 사용자별 우선순위 및 공정성을 관리하고, 백엔드 큐 길이를 모니터링하여 새로운 사용자의 대기 시간을 최소화하는 것이 핵심입니다.

핵심 포인트

  • LLM 추론 엔진은 GPU 효율을 위해 요청을 배치(batch) 처리하며, 이를 위한 백엔드 큐가 필요합니다.
  • 단순 FIFO 방식으로는 특정 사용자에게 트래픽이 집중될 경우 다른 사용자들이 불공평하게 지연되는 문제가 발생할 수 있습니다.
  • 진정한 공정성을 확보하려면 LLM-Server와 같은 상위 계층에서 사용자별 우선순위를 관리하고, 요청을 스케줄링해야 합니다.
  • 사용자 경험 개선을 위해 백엔드 큐 길이를 모니터링하여 새로운 사용자의 지연 시간을 최소화하도록 스케줄링 속도를 동적으로 조정하는 것이 중요합니다.
  • 공정성 기준은 단순히 '요청 수'뿐만 아니라, 요청이 LLM 자원을 점유하는 '처리 시간(생성 길이)'까지 고려해야 합니다.

vLLM 나 HuggingFace TGI 와 같은 추론 엔진은 다음으로 구성됩니다:

  • 요청에서 다음 토큰을 계산하는 실제 작업을 수행하는 워커 (worker)
  • 처음 도착했을 때 요청이 추가되는 큐
  • 큐에서 요청을 가져와 워커로 이동시키는 스케줄러 (scheduler)

왜 여기에 큐가 필요한가요? GPU 의 계산을 개별 요청이 아닌 배치 (batch) 방식으로 수행할 때 성능과 리소스 효율성이 더 뛰어나기 때문입니다. 이 백엔드 큐는 스케줄러가 여러 요청을 선택하여 같은 배치에 배치하여 처리하도록 허용합니다.

일반적으로 각 추론 엔진은 단일 모델만 서비스하며, 우리는 다른 모델을 병렬로 실행하는 여러 배포를 가지고 있습니다.

단일 사용자 "A" 가 많은 수의 요청을 보낼 때, 큐는 빠르게 채워집니다. "B" 와 "C" 같은 다른 사용자는 자신의 요청을 짧은 시간 후에 보내도 동일한 모델을 사용할 수 없습니다 (사용자 "A" 의 모든 요청이 처리될 때까지). 이 그림은 vLLM 을 추론 엔진으로 초점을 맞추고 있지만, 문제는 더 일반적이며 다른 백엔드에도 적용됩니다.

TNG 에서 사용자들은 vLLM 백엔드로 직접 요청을 보내지 않지만 API 서버 (우리가 "LLM-Server"라고 부름) 로 요청을 보냅니다. 여기서는 각 사용자 (및 모델) 에 대해 별도의 큐를 가질 수 있으며, FIFO(첫 번째 들어오기 - 첫 번째 나가기) 가 아닌 모든 사용자 큐를 순환하는 스케줄러가 있습니다. 이는 일부 공정한 스케줄링을 달성합니다: 예를 들어, 위의 그림에서 사용자 B 와 C 는 사용자 A 의 첫 번째 요청이 이미 스케줄링되었을 때 조금 더 늦게 자신의 요청을 보냅니다. 그 시점에서 사용자 A 에서 세 개의 요청이 이미 일정 시간 동안 대기 중이었지만, 사용자 C 는 사용자 A 의 한 요청이 완료될 때까지 기다려야 합니다.

핵심 아이디어는: 우리 자체 구성 요소에서 다른 사용자의 요청을 우선시하고 추론 백엔드에서는 아닙니다! 일반적으로, 요청이 추론 엔진에 보내진 후 그 순서는 변경할 수 없으므로, LLM-Server 에서 완전한 통제권을 가지고 있을 때 올바른 순서로 가져와야 합니다.

"공정한" 스케줄링이 무엇인지 결정하기 위해 다른 측면을 고려할 수 있습니다. 위의 예에서, 단일 요청을 가진 새로운 사용자는 연속으로 두 번 이상 서비스된 사용자보다 먼저 서비스되어야 합니다. 이는 "요청 수" 수준에서의 "공정성"입니다. 하지만 처리 시간도 고려할 수 있습니다: 요청이 백엔드를 얼마나 오랫동안 점유하는가? 긴 프롬프트와 긴 생성은 다른 사용자에게 더 오랜 시간을 LLM 을 "차단"하므로, 짧은 요청이 우선권을 가져야 할 수도 있습니다. 불행히도 생성 길이를 추정하는 것은 매우 어렵습니다. 일부 요청에는 "max_tokens" 제한이 있지만, 대화형 AI 어시스턴트에서 일반적인 채팅 메시지는 토큰 제한이 없으며, 매우 짧은 생성 ("이 텍스트 요약") 에서 매우 긴 생성 ("이야기 말해" / "xyz 의 모든 코드 작성") 까지 다양합니다.

프롬프트에 따라 요청을 유사성에 기반하여 배열하는 데 일부 이점이 있을 수 있으므로, vLLM 이 캐시 히트를 최대화할 수 있습니다. 이는 성능을 향상시킵니다. NVIDIA Dynamo 와 AIBrix 와 같은 프레임워크에서 최근 이러한 KV-cache-aware 라우팅에 주목받았습니다.

비즈니스 컨텍스트와 호스팅된 LLM 의 경우, 개별 요청의 비용은 고려할 수 있는 또 다른 지표이지만 유사한 도전과제를 가지고 있습니다.

해결책은 사용자 (및 모델) 당 하나의 큐가 아닌, 우선순위가 다른 여러 개의 큐를 갖도록 확장할 수도 있습니다. 예를 들어, 채팅 인터페이스를 갖춘 TNG AI Assistant 같은 상호작용 애플리케이션은 5 초 이내에 진행 상황을 보지 않는 사용자는 애플리케이션이 고장 났다고 생각할 것이므로 더 높은 우선순위를 가져야 합니다. 반면, 수십 개의 파일과 수천 줄의 코드를 생성하는 코드 리뷰를 수행하는 사용자들은 LLM 요청이 다소 시간이 걸릴 것을 기대합니다. 그리고 일부 사용 사례 (예: 배치 API 를 통해 스케줄링되는 벤치마크 실행) 는 다른 사용 사례를 방해하지 않고 아무것도 실행되지 않을 때만 스케줄링되도록 매우 낮은 우선순위를 가져야 합니다.

LLM-Server 측의 스케줄러가 모든 요청을 (공정 우선순위에 따라) 즉시 백엔드로 보낸다고 가정해 봅시다. 사용자 A 가 많은 요청을 동시에 보내고, 얼마 후 새로운 사용자 C 가 단일 요청을 스케줄링하고 싶어 합시다. 모든 사용자 A 의 요청이 다시 FIFO 큐에 쌓여버릴 것입니다. 새로운 사용자 C 는 이전에 받은 모든 요청이 처리될 때까지 기다려야 합니다. LLM-Server 가 없으면 초기 시나리오에는 거의 개선이 없을 것입니다.

이상적으로는 백엔드 큐의 최대 요소 수를 제한할 수 있지만, vLLM 은 이를 허용하지 않습니다. 따라서 우리는 LLM-Server 측의 스케줄러가 백엔드로 새로운 요청을 보내는 속도를 동적으로 조정해야 합니다. 여기서 우리의 목표는 새로운 사용자들이 경험하는 지연 시간을 최소화하기 위해 백엔드 큐 길이를 짧게 유지하는 것입니다.

(가장 간단한 접근 방식은 정적 속도 제한이지만, 대부분의 요청이 짧고 다른 모델과 로드 패턴에 대해 조정하기 어렵습니다.)

LLM-Server 에서 백엔드 큐 길이를 확인하려면 vLLM /metrics 엔드포인트에서 해당 Prometheus 지표를 가져와야 합니다. 우리의 공정 스케줄러는 백엔드 큐 길이 지표가 3 보다 작을 때만 백엔드로 요청을 보낼 수 있습니다. 예를 들어, 새로운 사용자의 지연 시간을 더욱 단축하기 위해 백엔드 큐 길이를 낮출 수 있지만, 이는 과다 활용되지 않는 배치와 효율성 감소를 초래할 때까지입니다 - 여기에는 트레이드오프가 있습니다. 다만, 목표 큐 길이는 최대 동시성을 반영하지 않는다는 점을 기억하세요. 동시에 3 개 이상의 요청이 처리될 수도 있습니다. vLLM 은 충분한 공간이 있을 때 ("연속 배치") 현재 배치에 큐 요청을 추가합니다.

LLM-Server 에서 백엔드 큐와 스케줄러 간의 피드백 루프를 구축하면, 스케줄링 결정에 사용되는 vLLM 지표 집합을 쉽게 확장할 수 있습니다. 예를 들어, TNG 의 상호작용 AI Assistant 를 위한 좋은 사용자 경험을 위해 우리는 높은 토큰 생성 속도 (예: >7 토큰/초, 즉 ~150ms 당 토큰) 를 목표로 하므로, 보고된 토크당 출력 시간 지표가 150ms 를 초과하면 새로운 요청은 스케줄링되지 않습니다.

또한 다른 우선순위의 요청에 대해 다른 지표 임계값을 프로그래밍할 수 있습니다. 예를 들어, 배치 API 에서의 낮은 우선순위 요청은 백엔드 큐가 완전히 비어있을 때만 요청을 스케줄링합니다: 우리는 더 높은 우선순위를 가진 이후 요청의 지연 시간을 증가시키는 것보다 GPU 의 단기 과다 활용 부족을 감수하는 것이 좋습니다.

최적화의 잠재력은 상당합니다: 예를 들어, 세 번째보다 짧은 백엔드 길이를 허용하고 현재 지표가 0 인 경우, 다시 지표를 가져와야 할 때까지 백엔드에 세 개의 요청을 즉시 보낼 수 있습니다. 최악의 경우, 어느 것도 현재 배치에 적합하지 않고 모두 백엔드 큐에 있는 경우 (이때 새로운 길이는 세로, 충분히 좋습니다).

최근 vLLM 은 FIFO 를 유지하기 대신 우선순위 기반 스케줄링 옵션을 추가했습니다. 이 새로운 기능은 요청이 백엔드로 보내지기 전에 우선순위로 태그를 붙입니다. 그리고 vLLM 은 더 높은 우선순위의 요청이 큐에 있는지 정기적으로 확인하고, 모든 요청 (실행 중인 배치와 대기 중인 큐) 을 우선순위에 따라 정렬합니다. 이는 우선순위 요청을 문자 그대로 큐에서 뛰어넘게 하고, 심지어 처리된 배치로 직접 이동하게 할 것입니다. 비용은 (정렬에 의한 일부 오버헤드 제외) 낮은 우선순위의 요청이 실행 중인 배치에서 추방되어 다시 대기 중인 큐로 돌아오게 된다는 점입니다.

vLLM 은 요청 우선순위를 연속적인 숫자로 이해합니다. 이는 기본, 높은 우선순위 (대화형 AI 어시스턴트), 낮은 우선순위 (배치 API) 를 구별하는 것뿐만 아니라, 사용자가 이미 백엔드로 보낸 응답이 대기 중인 모든 요청에 대해 더 작은 감량을 할 수 있습니다. 예를 들어, 사용자 B 와 C 는 단일 요청만 보내며, 이는 우선순위가 0 입니다. 사용자 A 는 네 개의 요청을 한 번에 보내며, 첫 번째는 우선순위가 0 이지만, 다음 ones 은 우선순위가 1, 2, 3 을 가지며, 나중에 처리됩니다 (더 높은 숫자 = 더 낮은 우선순위).

여전히 몇 가지 주의사항이 있습니다:

  • 백엔드 우선순위 기능은 vLLM 에만 제공되며, HuggingFace TGI 에는 제공되지 않습니다.
  • 스케줄러가 LLM-Server 측에 있으면, 시간당 출력 토큰 (time-per-output-token) 을 기반으로 스케줄링 속도를 조정할 수 있습니다. 백엔드 우선순위 스케줄링은 스케줄링 속도를 제어하지 않습니다.
  • 큐와 배치의 빈번한 재정렬이 지연 시간에 미치는 영향은 실제 부하 시나리오에서 측정해야 합니다.

전반적으로, 백엔드 측 우선순위 스케줄링은 vLLM 기반 시스템에 좋은 전략일 수 있습니다. 왜냐하면 이는 상류 LLM-Server 의 큐잉 논리를 단순화하기 때문입니다. 불행히도, 현실적인 설정에서는 개별 요청에 우선순위를 할당하는 객관적인 인스턴스를 필요로 하므로 LLM-Server 를 추가 스케줄링 레이어로 제거할 수 없습니다.

큐잉과 스케줄링은 다양한 우선순위의 사용자 및 클라이언트를 가진 애플리케이션에 매우 중요하며, 이는 사용자 경험에 큰 영향을 미칩니다. 특히 여러 클라이언트가 병렬로 요청을 제출하는 시나리오에서는 전용 "공정한 스케줄링" 전략에서 혜택을 받습니다. 백엔드 기능과 같은 우선순위 스케줄링은 최적화를 단순화할 수 있지만, 이러한 복잡성을 완전히 관리하려면 상류 게이트웨이 서버가 여전히 필요합니다.

이 블로그 포스트 시리즈의 다음 부분에서는 토큰 생성 (token generation) 에 초점을 맞추고, prefill 및 decode 단계 동안 추론 엔진에서 논의합니다. 특히, 자원 활용과 여러 요청의 동시 처리 전략에 대해 논의할 것입니다.

AI 자동 생성 콘텐츠

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

원문 바로가기
2

댓글

0