1. 요청 하나가 지나는 프로세스와 경계
LLM 서빙 프레임워크를 처음 열면 대개 스케줄러부터 찾는다. 하지만 스케줄러 코드를 아무리 읽어도 "이 함수는 누가 부르고, 저 큐에는 누가 넣는가"가 풀리지 않는다. Mini-SGLang 은 한 프로세스짜리 프로그램이 아니기 때문이다. HTTP 요청 하나가 답으로 돌아오기까지 최소 네 종류의 프로세스…
sgl-project/mini-sglang
을
커밋 9a91cfa 에 고정해 놓고 여덟 편에 걸쳐 읽습니다. 인용한 코드는 모두
그 커밋의 permalink 로 이어집니다.
HTTP 요청 하나가 답으로 돌아오기까지 지나는 프로세스 경계에서 시작해, Req/Batch 상태 장부, chunked prefill 예산, 페이지 할당, 접두사 재사용을 위한 radix cache 와 eviction, 두 스트림으로 CPU 스케줄링을 감추는 방법, 그리고 여러 TP rank 가 같은 결정에 도달하는 구조까지 따라갑니다.
각 편은 앞 편을 읽었다고 가정합니다. 1편부터 순서대로 읽는 편이 낫습니다.
LLM 서빙 프레임워크를 처음 열면 대개 스케줄러부터 찾는다. 하지만 스케줄러 코드를 아무리 읽어도 "이 함수는 누가 부르고, 저 큐에는 누가 넣는가"가 풀리지 않는다. Mini-SGLang 은 한 프로세스짜리 프로그램이 아니기 때문이다. HTTP 요청 하나가 답으로 돌아오기까지 최소 네 종류의 프로세스…
1편에서 스케줄러가 받은 것은 CPU 위의 1차원 int32 텐서였다. 그 토큰열이 답으로 자라는 동안, 시스템은 "이 요청이 어디까지 갔는가"를 어딘가에 적어 두어야 한다. 그 장부가 Req 다. 장부라고 부르는 이유가 있다. 필드가 많아서가 아니라, 길이 세 개로 진행 상태를 표현하고 그 셋의 대소…
2편에서 요청 하나는 세 길이로 표현되는 장부가 됐다. 이제 질문은 하나다. 대기 중인 요청이 여럿일 때, 이번 스텝에 무엇을 얼마나 넣을 것인가. 답은 예산 두 개와 조건 다섯 개다. 그리고 예산을 넘는 프롬프트를 어떻게 처리하는지에 이 저장소의 깔끔한 선택이 하나 더 있다. 예산은 둘이다 배치를 짜는…
3편에서 요청 하나가 "자리를 잡았다"고 했다. 그 자리의 정체가 이 편의 주제다. KV cache 는 페이지 단위로 관리된다. 그런데 요청마다 하나씩 있는 pagetable 을 열어 보면 페이지 번호가 아니라 토큰 하나하나의 위치가 적혀 있다. 페이지로 잡으면서 왜 표에는 토큰 위치를 적는가. 저장소가…
4편에서 요청 하나가 자기 자리를 잡는 법을 봤다. 그런데 같은 시스템 프롬프트로 들어오는 요청 백 개가 각자 같은 계산을 반복하면 그 자리는 금방 동난다. 그래서 이미 계산한 접두사를 다시 쓴다. 다시 쓰기 시작하면 두 가지 질문이 새로 생긴다. 어디까지 같은가를 어떻게 빠르게 찾는가, 그리고 공간이…
지금까지 다섯 편에 걸쳐 스케줄러가 하는 일을 봤다. 메시지를 받고, 예산을 계산하고, 트리를 걷고, 페이지를 잡고, 표를 채운다. 전부 CPU 일이다. 그동안 GPU 는 무엇을 하고 있는가. 아무것도 안 하고 있다면, 모델을 아무리 빨리 돌려도 그 사이사이가 비어 있다는 뜻이다. 이 편은 그 빈틈을 메…
6편 마지막에 preparebatch 의 일곱 줄을 열거하면서 마지막 두 줄을 남겨 뒀다. padbatch 와 preparemetadata 다. 이 편이 그 둘을 연다. 지금까지 세운 것은 전부 파이썬 객체였다. Req 의 길이 세 개, 슬롯 번호, page table 의 한 행. 커널은 그런 것을 모른…
1편에서 스케줄러가 TP 크기만큼 뜬다는 것을 보고, 그 뒤 여섯 편은 하나의 스케줄러 안에서 일어나는 일을 읽었다. 이제 그 전부를 rank 수만큼 복제한다. 여기서 문제가 생긴다. 스케줄러는 매 스텝 결정을 내린다. 어떤 요청을 배치에 넣을지, 어느 페이지를 잡을지, 언제 무엇을 버릴지. rank 들…