O que é, afinal, um mecanismo de inferência? — Charles Frye, Modal
Autoria: AI Engineer · Assistir no YouTube
Vídeo de autoria do canal indicado, não da IANIA; todos os direitos pertencem ao criador. Transcrição, resumo e tradução gerados automaticamente; o conteúdo no idioma original é preservado na versão em idioma original.
Idioma original: inglês
Vídeo original: What Is an Inference Engine, Anyway? — Charles Frye, Modal · Canal: AI Engineer · 59:35 · Assistir no YouTube
Tópicos: Agentes de IA, Avaliação e benchmarks de IA, Inferência e otimização de modelos, Infraestrutura e hardware de IA
Charles Frye explica como mecanismos de inferência transformam entradas em tokens de saída e por que seu desempenho depende tanto do escalonador quanto do código executado na GPU. A palestra compara cargas de trabalho de chatbots, agents em segundo plano e processadores de dados, destacando as diferenças entre prefill e decode. Em seguida, percorre a arquitetura dos mecanismos e técnicas como KV cache, CUDA graphs e speculative decoding. Por fim, aborda observabilidade, depuração em produção e recursos para estudar implementações de inferência.
Pontos-chave
Seção intitulada “Pontos-chave”- A inferência é um centro de receita, enquanto o treinamento costuma ser um centro de custo, o que cria oportunidades para engenheiros de inferência.
- As cargas de trabalho de chatbots, agents em segundo plano e processadores de dados diferem em latência, reutilização de prefixos e demanda por throughput.
- Prefill e decode têm perfis computacionais distintos e podem exigir estratégias diferentes de otimização.
- Um mecanismo de inferência combina processos de entrada e saída, tokenização, escalonamento e execução do modelo em aceleradores.
- O escalonador pode se tornar um gargalo no host por controlar a distribuição de trabalho à GPU.
- KV cache reduz recomputações, e sua capacidade e organização influenciam o desempenho.
- CUDA graphs reduzem o trabalho necessário no lado da CPU para iniciar operações na GPU.
- Speculative decoding pode acelerar a geração ao verificar várias previsões de tokens em paralelo com o modelo-alvo.
- Logs, métricas, traces e evals ajudam a identificar problemas de qualidade e regressões de desempenho em produção.
- Mini SGLang e Nano vLLM são apresentados como implementações acessíveis para aprender a arquitetura de mecanismos de inferência.
Capítulos
Seção intitulada “Capítulos”| Início | Capítulo | Resumo |
|---|---|---|
| 00:12 | Objetivo da palestra e visão geral | Charles Frye apresenta a proposta de explicar o que acontece dentro de um mecanismo de inferência e por que o desempenho é desafiador. |
| 03:19 | Por que trabalhar com inferência | A palestra compara a inferência ao treinamento e destaca a demanda comercial e a amplitude técnica do trabalho. |
| 07:28 | Principais cargas de trabalho | O palestrante distingue chatbots com ferramentas, agents em segundo plano e processadores de dados. |
| 09:34 | Prefill, decode e métricas | A apresentação descreve o percurso de uma requisição e métricas como throughput, tempo até o primeiro token e latência entre tokens. |
| 15:44 | Arquitetura do mecanismo de inferência | Frye separa o servidor de entrada e saída, o processamento de tokens, o escalonador e a execução do modelo. |
| 20:58 | Processos, escalonador e ciclo de requisição | A palestra explica como os componentes coordenam filas, lotes e execução na GPU, e por que o escalonador pode limitar o sistema. |
| 29:21 | Tokenização e concorrência | O palestrante discute o custo do processamento de entradas longas, a relação entre tokenizadores e detokenizadores e o uso de processos separados. |
| 36:45 | Código do modelo e bibliotecas de kernels | A apresentação percorre kernels, backends, código de passagem direta do modelo e construção de lotes. |
| 44:33 | Concorrência e técnicas de desempenho | Frye aborda o escalonamento de requisições e técnicas como KV cache, CUDA graphs e speculative decoding. |
| 53:53 | Observabilidade e depuração em produção | A palestra trata de erros de qualidade e desempenho e recomenda logs, métricas, traces e evals para investigá-los. |
| 58:02 | Recursos para aprender e encerramento | O palestrante recomenda implementações e ferramentas para estudar mecanismos de inferência e encerra com informações sobre a Modal. |
Citações
Seção intitulada “Citações”Fazer algo de alto desempenho, para produzir tokens com boa economia, é a parte desafiadora. — 01:48
No fim, treinamento é um centro de custo e inferência é um centro de receita. — 04:53
É stateless se você não se importa com desempenho. Torna-se stateful quando você se importa. — 19:55
Na operação ideal de um servidor de inferência, não há fila. — 39:23
Conceitos e entidades
Seção intitulada “Conceitos e entidades”- inference engine (conceito): Sistema que agenda e executa a inferência de modelos, convertendo entradas preparadas em saídas para clientes ou sistemas externos.
- inference server (conceito): Camada que recebe requisições, integra o mecanismo de inferência e devolve respostas por interfaces como HTTP ou gRPC.
- prefill (conceito): Fase que processa os tokens de entrada e prepara o modelo para gerar a resposta.
- decode (conceito): Fase que gera tokens de saída, normalmente em etapas sequenciais.
- KV cache (conceito): Cache de valores intermediários da atenção que evita recalcular informações já processadas.
- CUDA graphs (conceito): Representação de uma sequência de operações da GPU como um grafo que pode ser lançado com menos trabalho de controle na CPU.
- speculative decoding (conceito): Técnica que usa um modelo para propor tokens e outro para verificá-los em paralelo, acelerando a geração sem alterar o resultado esperado.
- SGLang (ferramenta): Mecanismo de inferência cuja arquitetura e técnicas de otimização são discutidas na palestra.
- vLLM (ferramenta): Mecanismo de inferência citado na comparação de arquiteturas e como recurso para estudar implementações.
- Modal (empresa): Empresa e plataforma na qual Charles Frye trabalhou com implantação e otimização de inferência.
- Charles Frye (pessoa): Palestrante que apresenta a arquitetura, as cargas de trabalho e as técnicas de otimização de mecanismos de inferência.
Tradução completa (pt-BR)
Seção intitulada “Tradução completa (pt-BR)”00:12 Certo. Vou começar enquanto as pessoas se acomodam. Temos um cronograma bem apertado. Há muitas oficinas realmente maravilhosas neste evento, então quero garantir que tenhamos bastante tempo. Reservei de 10 a 15 minutos para perguntas e acho que seria melhor, especialmente por ser uma oficina, se vocês simplesmente me interrompessem. Quando eu estiver falando, levantem a mão, que eu chamo vocês e respondo à pergunta. Então, vamos manter isto
00:44 interativo. Acho que é melhor assim. Conversei com algumas pessoas, e parece que há bastante gente aqui que entende de inference, que conhece os mecanismos, então podemos ter uma conversa sofisticada. O título desta palestra é “Afinal, o que é um mecanismo de inference?”. A ideia é revelar algumas das camadas por trás das APIs e o que realmente acontece dentro do
01:16 código que executa a inference quando vocês mesmos a executam. De forma geral, tokens entram e tokens saem, e muitas vezes vocês não precisam pensar no que acontece no meio. Há softwares open source de alta qualidade para executar a inference de modelos de linguagem ou multimodais. Temos SGLang, vLLM e TensorRT-LLM. Muitas pessoas escrevem coisas parecidas com essas internamente. Elas projetam esses mecanismos que, em alto nível, recebem alguns tokens e produzem alguns tokens, o que não é tão difícil.
01:48 Dá para escrever algo assim usando PyTorch e Hugging Face Transformers em uma ou duas horas, especialmente se vocês forem bons com agents. Mas fazer isso com desempenho extremamente alto, para produzir tokens com uma boa economia de tokens, é a parte desafiadora. E isso, de certa forma, orienta o projeto e a arquitetura desses sistemas.
02:19 Só para fazer um esboço bem rápido antes de voltarmos ao panorama geral e falarmos sobre por que estamos aqui: em geral, esses sistemas são arquitetados como um conjunto de processos que se comunicam. Um deles faz a entrada e saída, fornece serviços via HTTP ou RPC e responde ao mundo externo. Depois, há um pouco de pré-processamento e pós-processamento com tokenizers e detokenizers. Isso envolve o
02:49 núcleo do sistema, onde o trabalho de fato é feito, dividido em duas partes. Uma é uma espécie de escalonador, que define e agenda o trabalho para ser executado no acelerador, geralmente uma GPU. A outra é o código que realmente roda na GPU. Esses dois componentes, no canto inferior direito, são responsáveis pela maior parte do trabalho sensível ao desempenho e pelos problemas mais complexos.
03:19 [fungada] É sobre isso que passaremos a maior parte do tempo conversando. Certo. Com esse contexto estabelecido, acho que venho à AI Engineer desde que era a Summit e, a cada edição, falo sobre executar inference. E a resposta a esta pergunta fica cada vez melhor. A pergunta é: por que inference, por que vocês deveriam se importar com isso e por que deveriam pensar
03:50 em ser um engenheiro de inference que opera mecanismos de inference? Nosso destemido líder e exército de conferência de um homem só, Sean Wang, publicou recentemente no Twitter que todo mundo na infraestrutura de IA — a parte chata, como os mecanismos de inference, não a pesquisa sexy — está finalmente ficando podre de rico. Acho que o que ele está apontando é que há uma demanda enorme para executar inference. Há
04:22 demanda nos laboratórios, há demanda dentro das empresas que querem ter suas próprias stacks. Há demanda para montar e repensar a stack de infraestrutura, e tudo isso envolve essa carga de trabalho central de inference. Mas Sean também está apontando que, até agora, muita gente tem pensado sobretudo no lado da pesquisa, na produção de modelos. Então, por que não pensar em treinamento? Por que não falar sobre os mecanismos de treinamento durante o seu precioso tempo? Acho que o motivo fundamental
04:53 pelo qual este é um lugar tão importante para fazer trabalho de engenharia, e um lugar tão legal para trabalhar como engenheiro, é que, no fim das contas, treinamento é um centro de custos e inference é um centro de receita. Quando se treinam modelos, acontece que, em geral, as pessoas não conseguiram fazer negócios funcionarem vendendo diretamente os pesos dos modelos. Portanto, não dá para transformar uma estrutura de treinamento em um sistema realmente autossustentável. Mas inference, depois que se tem um
05:25 modelo, é algo pelo qual as pessoas pagam. Elas pagam por um sistema que receba seus tokens e seu contexto para produzir novos tokens, criar software para elas, seja o que for. Há muitas oportunidades para fazer inference. Montamos um cenário em que há um pequeno número de empresas que produzem modelos fundacionais, que depois são implantados e personalizados por engenheiros de inference. E até no treinamento inference acabou entrando. Grande parte da
05:56 adaptação pós-treinamento desses modelos fundacionais exige que se gerem amostras do modelo por inference, e muitas das mesmas tecnologias entram em jogo. Muitas das mesmas preocupações estão presentes. E, por fim, pensando de forma mais egoísta, como engenheiro, inference ainda é algo muito novo, embora já estejamos há anos nessa nova tecnologia. Assim, você acaba trabalhando em toda a stack, da arquitetura de aplicações à álgebra linear, chegando até os elétrons e o hardware. Então,
06:26 é um playground para engenheiros. Vamos ver bastante disso ao longo da conversa. Talvez, para ganhar tempo, já que tivemos que começar muito rápido, eu pule esta parte. Trabalho com inference há alguns anos, principalmente na Modal, depois de vir do mundo do treinamento, e ajudei muitas equipes a implantar e otimizar sua inference em centenas ou milhares de GPUs na plataforma Modal. Também criei muitas aplicações de inference. Esta é uma pequena obra de arte que fiz. Na semana passada, ela esteve
06:58 no Legion of Honor. Esta obra de arte capta o que vê, passa a imagem por um modelo de linguagem e visão — um dos modelos Qwen executado por meio do mecanismo de inference SGLang na Modal — e descreve a imagem como se fosse uma obra de arte. Assim, produz uma daquelas plaquinhas de museu. Como todo mundo sabe, algo só é arte depois que alguém escreve uma dessas plaquinhas para descrever o que você fez. Apontei a câmera para a fila
07:28 na entrada, e ela criou uma descrição bem bonitinha. Enfim, esse é o tipo de aplicação de inference que fica mais ou menos no meio do espectro, entre as aplicações de menor latência e as de maior throughput que são implantadas. Vou começar falando com mais detalhes sobre essas cargas de trabalho, como os tipos de carga que chegam a esses mecanismos de inference, e depois passar rapidamente para a arquitetura: como esses mecanismos são projetados e algumas das principais técnicas que se encaixam nessa
07:58 arquitetura para melhorar o desempenho. Por último, um elemento essencial para fazer essas coisas funcionarem em produção: a observabilidade e a facilidade de depuração desses mecanismos, tanto em relação à correção quanto ao desempenho. Certo, vamos começar rapidinho pelas cargas de trabalho. Gosto de dividir as coisas em três partes. Há o chatbot e seus derivados, que são o caso de uso original que popularizou a inference, como ChatGPT ou Claude Code. Do outro lado, há uma pessoa esperando e interagindo com essa inference
08:30 sistema e fornecendo contexto adicional. O sinal de mais indica que ele não apenas conversa com uma pessoa, mas provavelmente também interage com sistemas externos por meio de chamadas de ferramentas. Depois, temos os agents em segundo plano. Eles costumam realizar tarefas semelhantes às de algo como o Claude Code, mas fazem isso em segundo plano, não diretamente em resposta a uma pessoa. Alguns exemplos são Devin, Ramp Inspect e OpenClaw; para um caso que não envolve programação, temos o Harvey como agent em segundo plano. Isso se caracteriza por oferecer recursos ou cargas de trabalho semelhantes, mas com um perfil de desempenho
09:03 diferente. E, por último, temos os processadores de dados. Em geral, eles executam modelos relativamente pequenos. Recebem dados não estruturados, como um PDF, e os transformam em dados estruturados. Extraem algumas informações desses dados, geralmente algo que você poderia inserir em um banco de dados. Um exemplo é a plataforma de processamento de documentos Reducto, ou algo como o Fathom, que recebe um vídeo e extrai a transcrição. Certo, então, nos bastidores, como são essas
09:34 cargas de trabalho? Você envia alguns tokens de um cliente, e eles chegam a algum servidor. Por enquanto, vamos nos concentrar na implantação de inference na nuvem, porque é onde a maior parte dela acontece atualmente. Obviamente, no futuro, muitas dessas engines de inference vão acabar sendo executadas nas máquinas das pessoas, daqui a alguns anos. Sim, pode falar. >> O tamanho da tela. >> O brilho da tela. Sim, então
10:04 a pergunta era sobre ajustar o brilho. Não sei se conseguimos corrigir isso. É o que acontece quando faço slides no modo escuro, mas enfim. Certo. Um cliente envia uma entrada, que é então processada pela nossa engine, em algum servidor. Primeiro, processamos os tokens de entrada na fase de pré-preenchimento e, em seguida, geramos os tokens de saída na fase de decodificação. Uma execução de pré-preenchimento faz uma passagem direta pelo
10:36 modelo; depois, várias execuções de decodificação produzem um ou alguns tokens de cada vez. Quando terminamos, retornamos esses tokens ao usuário ou geramos uma chamada de ferramenta e interagimos com um sistema externo. Então, esses dois aspectos — a interface externa e essa divisão interna entre pré-preenchimento e decodificação — definem as métricas que importam para uma carga de trabalho. Elas indicam como implantar sua engine de inference e como medir seu sucesso e desempenho. Por exemplo, você tem
11:07 algo como consultas recebidas por segundo e consultas produzidas por segundo. Essa métrica é bem difícil de controlar, porque depende dos usuários. Provavelmente é mais fácil pensar nela no nível da engine: quantas consultas por segundo você consegue atender por réplica da sua engine de inference. Depois, você tem a demanda agregada dos usuários. Para essas consultas, você quer observar quantos tokens há por consulta, tanto na entrada quanto na saída — ou seja, no pré-preenchimento e na decodificação. Então,
11:38 essa métrica é difícil porque não depende apenas dos usuários, mas também do modelo. A decisão de quando terminar o processamento de uma consulta não está sob seu controle determinístico. Ela fica a cargo do modelo dentro da engine de inference. Portanto, como essas duas fontes de não determinismo estão envolvidas, é preciso medir isso e executar um benchmark para obter a métrica. E, durante o processamento, produzimos artefatos intermediários que queremos armazenar em cache no KV cache. O
12:09 grau de reutilização desse cache é um componente fundamental da definição da nossa carga de trabalho. Isso também depende principalmente dos usuários, mas, conforme a carga de trabalho, pode haver padrões de reutilização de prefixos relativamente fixos. Por fim, vindo da camada da aplicação, você tem um orçamento de latência, algum SLO que define a rapidez com que deseja retornar os tokens. Isso pode ser a rapidez com que você conclui algo como a fase de pré-preenchimento
12:39 e deixa os primeiros tokens prontos para enviar aos usuários. Ou pode ser a rapidez com que você consegue gerar cada conjunto de tokens durante as etapas de decodificação e devolvê-los aos usuários. Essas são a latência até o primeiro token e a latência por token de saída, ou latência entre tokens. Essa será a principal restrição que você terá de levar em conta. Certo. Podemos relacionar isso aos nossos arquétipos de aplicação. No caso de um chatbot com um agent em segundo plano, há uma reutilização de prefixo muito alta. Existe alguma interatividade, seja com uma pessoa ou com um
13:10 agent. Em geral, o chatbot com um agent em segundo plano tem decodificações relativamente curtas e saídas relativamente curtas, de dezenas a centenas de tokens. E a tolerância à latência é muito baixa. Uma pessoa quer ver algo em poucas centenas de milissegundos, no máximo; caso contrário, fica entediada, irritada e vai embora. Com um agent em segundo plano, talvez você tenha alguns minutos para produzir um PR, certo? Se a qualidade for tão alta quanto a de um engenheiro, talvez semelhante à de um bom engenheiro de software, você pode ter algumas horas. Portanto, o orçamento para
13:40 milissegundos de sobrecarga de rede, ou qualquer outra coisa, não é tão apertado. Já no caso de um processador de dados, geralmente há pouquíssima reutilização de prefixo. A cada vez, você recebe documentos diferentes. Talvez haja um system prompt, mas, em geral, ele é pequeno em relação às outras entradas. A decodificação também é muito curta. Você produz um objeto estruturado que, em geral, é curto em relação à entrada. A versão estruturada é mais curta do que a versão não estruturada. E, nesse caso, a demanda costuma ser menos por latência e mais por throughput agregado, porque
14:11 geralmente você está processando rapidamente uma tarefa de preenchimento retroativo de banco de dados, entre outras coisas. Certo. Quero destacar que, depois de analisar tudo isso, dá para ver que, embora na camada da aplicação pareça uma única carga de trabalho — da perspectiva do usuário, é uma só —, quando começamos a examinar o que a engine está fazendo, parece haver duas subcargas de trabalho. Há a carga de trabalho de fazer o pré-preenchimento, de processar
14:42 os prompts de entrada. E há o trabalho de fazer as decodificações para produzir as saídas. Essas duas partes operam de forma relativamente independente dentro da engine. Por isso, exigem perspectivas diferentes e, muitas vezes, seguem caminhos de código diferentes. A diferença também se deve ao fato de que, na fase de decodificação, para cada usuário, há muito menos operações aritméticas, muito menos cálculos por decodificação e por usuário do que na fase de pré-preenchimento, que tem uma intensidade aritmética muito maior e exige muito mais cálculos por solicitação. Essa é a restrição fundamental, mas também o espaço em que você opera ao configurar uma engine, adicionar recursos ou ajustar o comportamento dela para atender a essas duas necessidades. Certo, queria ter certeza de que estamos todos alinhados sobre alguns aspectos fundamentais do que essas engines realmente fazem antes de abrirmos o capô e começarmos a examiná-las.
15:13 fase de prefill, que se parece muito com uma intensidade aritmética bem maior, muito mais, ah, muito mais cálculos por solicitação. E essa é sua restrição fundamental, mas também o tipo de espaço em que você opera ao configurar um mecanismo e, depois, adicionar recursos ou ajustar o comportamento do mecanismo para atender a essas duas coisas. Certo, eu queria ter certeza de que estamos todos na mesma página quanto a alguns dos aspectos centrais do que esses mecanismos realmente fazem antes de abrirmos o capô e começarmos a examiná-los.
15:44 Então, qual é a arquitetura de uma engine de inference? Este é um pequeno diagrama que mostra, em alto nível, o que uma engine de inference faz. O objetivo de um servidor de inference é receber uma solicitação HTTP ou gRPC e transformá-la em uma resposta HTTP para o cliente. Ele faz isso passando por todas estas etapas aqui. A ideia deste diagrama é que, se você seguir qualquer conjunto de setas, chegará ao mesmo resultado. Então, você pode
16:16 Vamos descascar uma camada das setas, por assim dizer, e olhar por dentro. Então, o que é o servidor de inferência? Ele faz essa coisa de alto nível. Qual é o próximo nível abaixo? O motor de inferência é a parte interessante, por assim dizer: é a parte que não está fazendo o atendimento HTTP, porque não estamos numa situação em que o atendimento HTTP seja a parte difícil ou o gargalo. Não é como um proxy. Este é um caso em que você está lidando com dezenas, centenas, talvez milhares de solicitações e respostas por réplica, no máximo, e
16:47 então o hardware de uso geral dá conta disso com muita facilidade. O que realmente interessa está todo na camada abaixo. No topo fica a entrada e saída do servidor para as solicitações HTTP. Na parte de baixo fica o motor propriamente dito, que é como um conjunto de processos. E dá para dividir isso em três partes. A solicitação chega expressa em representações mais amigáveis para as pessoas ou externas. Então, você tem strings Unicode, imagens PNG, vídeos MP4 ou streams WebRTC. E
17:19 esses dados precisam ser pré-processados antes de serem enviados para a inferência propriamente dita. Eles são transformados em conjuntos de tokens, que depois são mapeados para tensores na GPU. Aí podemos executar a parte de inferência do motor de inferência. No caso mais simples, ela recebe um tensor com K entradas e retorna um tensor com K mais uma entradas. Esses são modelos de machine learning que preveem o próximo token: eles tentam adivinhar qual seria a próxima entrada nesse tensor grande, com base nos
17:50 dados que vi durante o treinamento, sejam dados da internet, dados sobre o que um agent útil faria, dados de RL ou qualquer outra coisa que tenha sido usada para aprender a prever essa próxima entrada do tensor. Esse é o núcleo. E o restante do motor de inferência é construído em torno dele. Depois, temos o pós-processamento aqui ao lado. O pós-processamento transforma esse tensor novo em uma resposta. Em geral, isso é feito por esse destokenizador, que primeiro volta para o espaço dos tokens e depois retorna à nossa representação amigável para as pessoas e para os sistemas externos, na resposta
18:23 para que ela possa ser enviada ao usuário. Pergunta. >> Você poderia explicar a diferença entre o servidor de inferência e o motor de inferência? E onde entra… >> Ótima pergunta. Sim. A pergunta foi: qual é a diferença entre um servidor de inferência e um motor de inferência, e onde algo como VLM se encaixa? Servidor de inferência significa que
18:53 você colocou alguma entrada e saída em torno das operações centrais, como receber solicitações e transformá-las em respostas. VLM tem os dois: funciona como um servidor HTTP ou gRPC. E a camada do motor é como uma parte do VLM, ou do SDK de SGLang, como VLM. A maioria das implantações tem a forma de um servidor com uma interface de solicitação única e resposta única, como mostrado aqui.
19:25 Sim, isso responde à sua pergunta? Sim. Legal. Eu já disse isso antes, mas algumas pessoas chegaram depois: por favor, interrompam e levantem a mão para fazer perguntas se alguma coisa não estiver clara ou se quiserem se aprofundar. Sim. >> Rom. >> Sim. A pergunta de Rustom é: essa carga de trabalho tem estado ou é sem estado?
19:55 A resposta é que ela não tem estado se você não se importa com o desempenho. Ela passa a ter estado quando você se importa com o desempenho, o que acontece praticamente em todos os casos. O principal estado que se acumula ao longo do tempo para as solicitações é o cache KV com coisas calculadas anteriormente para uma solicitação anterior, associado a alguma noção de sessão. O gerenciamento de coisas como sessões geralmente fica fora do servidor de inferência e do motor de inferência. Então,
20:27 uma réplica individual do motor pode acompanhar esse cache KV, mas cabe à camada que a envolve — que pode ser algo como Nvidia Dynamo ou LMD — lidar com a definição do que significa uma sessão. Nesse modelo, são os clientes que, por assim dizer, definem as sessões. E é aí que muita coisa fica complicada. Não vamos falar muito sobre isso, porque está meio fora do escopo; já temos bastante a discutir sobre os motores. Mas, sim, venham falar comigo depois se quiserem conversar sobre essa camada.
20:58 Certo. Já falamos deste diagrama antes, mas quero voltar a ele agora que vimos um pouco mais. Esta camada externa é o nosso processo de entrada e saída do servidor. Este diagrama mostra especificamente a arquitetura do SGA. Há algumas pequenas diferenças em VLM, mas esse processo de entrada e saída do servidor se comunica com o mundo externo e depois com o pré-processamento e o pós-processamento. Esses componentes se comunicam com um processo auler, que é o responsável por definir e agendar o trabalho na GPU e cuidar de coisas como o enfileiramento. E então temos o processo que ainda não está pronto para…
21:32 isso. E, então, um ou mais processos estreitamente ligados à GPU são os que fazem o trabalho de verdade da inferência. O ponto principal a destacar é que, se você é uma pessoa de ML da velha guarda e está nesse mundo há muito tempo, a camada de passagens forward do modelo é aquela com a qual você está familiarizado. É onde aparece a grande maioria do código de PyTorch e semelhantes. E esse é o componente — a GPU, ou o acelerador em geral — que custa mais e é capaz de realizar mais trabalho, em operações na escala de petaflops por segundo. É aqui que se concentra o trabalho intenso de otimização de desempenho, buscando ganhar microssegundos. Mas, ponto crucial: esse processo auler aqui, ou o equivalente em outra arquitetura, é o gargalo para esse componente. E, em geral, ele funciona… Ele é
22:05 literalmente single-threaded nas implementações sobre as quais vamos falar, mas também é single-threaded em termos semânticos. É esse componente que define o trabalho realizado na GPU. Alguém precisa definir esse trabalho. Alguém precisa gerenciar um monte de recursos ali, e há algumas coisas complicadas de fazer em concorrência. Então, isso se torna um ponto de controle single-threaded em termos semânticos que pode, apesar de não fazer muita coisa — não executar tantas
22:36 operações quanto o trabalho na GPU —, acabar sendo o seu gargalo. É como o porteiro da GPU. Por isso, esse é o outro ponto em que deve se concentrar a maior parte do trabalho de otimização de desempenho e dos cuidados no projeto e na operação de um motor de inferência. Vamos ver mais sobre isso à medida que avançarmos, mas essa é a principal conclusão sobre esses componentes. >> Então, a solução para esse gargalo
23:06 é…? Sim. A pergunta foi se a solução para esse gargalo seria executar os agendadores em paralelo ou melhorar alguns dos componentes externos que consomem o que ele fornece. Acho que, no estado atual das coisas, ainda não se chegou ao ponto em que não seja possível executar esses processos do servidor em Python; basta que o lado do host seja mais rápido que o lado da GPU.
23:36 E o aumento de complexidade que você tem ao tentar gerenciar locks ou coordenar os recursos da GPU é muito maior do que o ganho, porque, mesmo que você conseguisse reduzir o tempo para um microssegundo, o trabalho não começaria até que o trabalho anterior na GPU tivesse terminado. Isso depende da carga de trabalho específica, mas, em geral, estamos falando de dezenas a centenas de milissegundos. Então…
24:08 não há aquele aumento de complexidade que você teria ao tentar gerenciar locks ou coordenar os recursos da GPU; esse aumento de complexidade é muito maior do que o ganho, porque, mesmo que você conseguisse reduzir isso a um microssegundo, o trabalho não começaria até que o trabalho anterior na GPU tivesse terminado. E isso, você sabe, depende da carga de trabalho específica, mas geralmente estamos falando de dezenas a centenas de milissegundos. Então, é…
24:42 Em geral, na maioria das vezes, dá para contornar isso. Vamos falar um pouco mais sobre isso, então talvez pergunte de novo quando chegarmos a esse ponto, se isso não tiver sido satisfatório. Mas, sim, ótima pergunta. Mais perguntas boas, por favor. Certo. Então, olhando para isso, aquele era o diagrama de arquitetura que você associa à engenharia. Vamos olhar para o ciclo de vida de uma solicitação nessa arquitetura. O servidor recebe uma entrada do mundo externo. Ele diz: “Certo, temos uma solicitação”. Ele a envia ao tokenizador
25:14 para preparar ou pré-processar essa solicitação, que então vai para o escalonador, que decide: “Certo, quantos recursos isso vai consumir? Quais recursos estão disponíveis?”. E então isso executa este ciclo principal de criar lotes de solicitações para enviar aos executores do modelo. Em geral, a maioria dos aceleradores se beneficia de operar com várias solicitações em paralelo. Isso vale especialmente para GPUs, mas também para TPUs e muitos outros. Então, você quer reunir um
25:45 grupo de solicitações e executá-las. Se você trabalha com bancos de dados, talvez imagine: “Estou prestes a executar uma varredura sequencial, então é melhor pegar três ou quatro consultas que vão ler a tabela inteira e executar todas as consultas de uma vez”. Porque a parte difícil é aquela varredura sequencial, que carrega a tabela inteira. Então, algo parecido acontece aqui. Esse ciclo principal se repete: inicia um lote e produz os tokens e a distribuição de probabilidade sobre os tokens, os logits. Isso vem do executor ou dos executores do modelo. Esse processo funciona continuamente, por assim dizer. E, quando os tokens ficam prontos, podem ser passados ao destokenizador para serem transformados de volta em uma resposta adequada para o mundo externo e então enviados de volta ao servidor. Então, sim, o interessante nisso é que esse sistema é simples em comparação com o que acontece dentro de
26:15 bancos de dados, sistemas de arquivos e servidores web. Em parte, porque é relativamente novo, mas também porque há apenas este componente central no meio, que você quer fazer funcionar na velocidade máxima. Sim. Pergunta. >> A pergunta foi se há comunicação entre o tokenizador e o destokenizador. Até onde sei, não há comunicação direta entre o tokenizador e o destokenizador no SGLang. No VLM, na verdade, eles ficam dentro do mesmo
26:46 processo, o gerenciador, o gerenciador do mecanismo. Então, fica um pouco mais difícil determinar se há alguma comunicação entre eles. Mas, em geral, eles não precisam se comunicar. Só precisam operar essencialmente com o mesmo sistema para mapear entradas para tokens, ou tokens para saídas. Sim. Isso respondeu à sua pergunta? >> Sim.
27:17 Não tenho certeza. >> Certo. A pergunta era basicamente sobre como manter a coerência entre o tokenizador e o destokenizador e como lidar com entradas talvez ambíguas. A resposta é que, sim, eles executam
27:47 as mesmas bibliotecas subjacentes, geralmente a biblioteca tokenizers da Hugging Face. E eu diria que a parte de tokenização é mais difícil do que a de destokenização. A destokenização fica um pouco mais inteiramente sob o controle da pessoa que implementa o destokenizador. Sim. >> Software
28:19 >> Sim. A pergunta era se o software permite que o tokenizador e o destokenizador sempre tomem as mesmas decisões. Eu diria que sim. Estou tentando garantir que não esteja prometendo demais. Mas não acho que seja necessário haver comunicação entre eles para garantir que as mesmas decisões sejam tomadas. Sim. Pergunta. Desculpe, a pessoa. Sim. >> Sim.
28:50 >> Certo. Primeiro, foi observado que o tokenizador pode ser paralelizado e, em geral, é executado em vários processos. Ainda assim, o tempo até o primeiro token é maior quando as sequências de entrada são mais longas. Por quê? A resposta é que processar uma sequência mais longa de tokens leva mais tempo dentro deste ciclo principal. Esse é o principal fator. Se você tiver 100 mil tokens, isso vai levar, digamos, 10
29:21 vezes mais do que 10 mil tokens. Quando chega a um certo tamanho, o tempo é basicamente linear em relação ao tamanho da sequência de entrada. Depois, em um mecanismo de inferência em execução, também há efeitos de dividir os tokens de entrada em várias partes e executá-las como lotes separados. Então, também pode haver um acréscimo: se houver filas e congestionamento, isso aumenta o processamento de um lote longo em relação a um lote curto. Você pode sofrer o atraso da fila várias vezes. Mas talvez o ponto subjacente
29:51 a esclarecer seja que produzir o primeiro token significa produzir o primeiro token de saída, não o processo de tokenização. E, por fim, não era parte da pergunta, mas eu queria destacar algo. É interessante que, em geral, a arquitetura SGLang tenha vários processos de destokenização e um destokenizador. A explicação é que os tokens de entrada chegam a uma taxa muito maior do que a taxa em que os tokens de saída são produzidos. Isso está relacionado ao fato de que o prefill, em geral, pode operar com taxas totais de tokens por segundo muito
30:23 mais altas do que o decode. Então, não há tanta pressão sobre o componente de destokenização do sistema para operar tão rapidamente. Além disso, embora ele possa ser um gargalo para a resposta ao usuário, não é um gargalo para o recurso de passagem forward do modelo principal. Então, também fica um pouco fora do caminho crítico. Sim.
30:55 >> Sim. A pergunta era quanto da latência vem desta parte, no tokenizador, e quanto vem desta parte, no ciclo principal. Isso depende da sua carga de trabalho. Eu diria que houve uma observação interessante de, quem era mesmo? Acho que do Cruso, que apontou que os tokenizadores existentes geralmente não aparecem como gargalo quando você tem modelos com, digamos, 20 bilhões de parâmetros no total ou mais. Mas, se
31:27 você estiver operando um modelo menor e trabalhando com contextos de entrada muito grandes, ele pode aparecer como gargalo. Então, essa talvez seja uma boa regra geral. Eu diria que, na verdade, não precisei pensar muito na latência do tokenizador. Em geral, o prefill e, em grande medida, o decode são os gargalos de latência. No decode, se você estiver produzindo mil tokens de saída por segundo, cada um leva, em média, cerca de um milissegundo. Mas pode chegar a 10 ou 20
32:00 milissegundos, ou até mais, se você tiver uma taxa de decode menor, como em um modelo grande que esteja executando com menor interatividade. Então, os tokenizadores definitivamente ficam na escala de milissegundos ou menos, e isso só acontece uma vez por solicitação. Sim. Também vale notar que ninguém fala sobre cache de tokenizador. As pessoas falam sobre cache de KV, mas não sobre cache de tokenizador, embora fosse possível fazê-lo. Vou continuar para garantir que consigamos passar por mais coisas, mas estas foram ótimas perguntas. Então, se ainda for pertinente, por favor, perguntem. Outra pergunta importante: espere, por que estamos usando multiprocessamento? O principal motivo é que queremos threads de controle separadas para cada subcomponente: para a E/S do servidor, para a tokenização e a destokenização, e para cada executor do modelo. Queremos que eles possam operar de forma tão independente uns dos outros quanto possível. Muitos deles — o tokenizador e o destokenizador — fazem um trabalho relativamente intensivo em computação, em comparação com a maior parte do que acontece, talvez, em um servidor web. O executor do modelo está interagindo com a
32:32 milissegundos, ou até mais, se você tiver uma taxa de geração de tokens menor, como acontece com um modelo grande que está rodando com menor interatividade. E, portanto, os tokenizers definitivamente operam na escala de milissegundos ou menos, e isso só acontece uma vez por solicitação. Sim. Também vale notar que ninguém fala sobre cache de tokenizer. As pessoas falam sobre cache de KV, mas não sobre cache de tokenizer, embora fosse possível fazer isso. Vou continuar só para garantir que passemos por mais conteúdo, mas essas perguntas foram ótimas; então, se ainda fizer sentido, perguntem.
33:04 Outra pergunta importante: esperem, por que estamos usando multiprocessamento? O principal motivo é que queremos threads de controle separadas para cada subcomponente: para a E/S do nosso servidor, para a tokenização e a destokenização e para cada executor de modelo. Queremos que eles possam operar de forma tão independente uns dos outros quanto possível. Muitos deles — o tokenizer e o destokenizer — fazem um trabalho relativamente intensivo em computação em comparação com a maior parte do que acontece, talvez, em um servidor web. O executor de modelo, por sua vez, faz interface com o…
33:37 GPU ou acelerador. Ele interage com isso de uma maneira meio parecida com a forma como você interagiria com uma NIC em um servidor web, ou seja, você descarrega trabalho nela. Então, para essa parte, você dá a ela seu próprio loop de eventos. Talvez, em teoria, isso pudesse fazer parte de um loop de eventos mais amplo. Mas certamente é mais limpo ter esses fluxos de controle separados e otimizar suas latências; não há comunicação entre processos suficiente para exigir que eles, digamos, fiquem no mesmo
34:08 thread. Mas por que não usar threads para cada um deles, em vez de processos? Uma das razões fundamentais é que o Python é single-threaded por processo. Isso começou a mudar recentemente, mas ainda há muito trabalho a fazer em termos de compatibilidade e desempenho. Como o Python tem um bloqueio global do interpretador para todas as threads no mesmo processo, você frequentemente ficaria bloqueado por causa disso. Então, esse é outro motivo para separá-los. Mas isso só levanta a questão — ou, acho que
34:40 gera a questão: por que Python? A resposta é que o Python tem excelente suporte a modelos e GPU. É fácil executar código em aceleradores com Python. Especificamente, para esse componente de model forward pass. Mas, espera, por que o Python tem um excelente suporte aqui? Em parte, é porque é uma ótima linguagem para pesquisadores, por ser fácil de usar, e esses modelos ainda estão surgindo de um ambiente meio voltado à pesquisa e à ciência de dados. Mas há uma razão mais profunda: o Python realmente manteve uma interoperabilidade profunda com C e C++ como um de seus princípios centrais de design, e isso permite uma comunicação muito mais fácil com esses aceleradores e com código acelerado em geral do que uma linguagem talvez igualmente ergonômica, como JavaScript. Mas por que não reescrever isso em Rust? Os processos de model worker, de novo, estão apenas tentando dizer à GPU o que fazer. Então, o código na GPU poderia ser escrito em uma linguagem compilada e cuidadosamente otimizado até o limite
35:13 da sua capacidade. Mas os processos de model worker só organizam isso, e há alguns truques, como CUDA graphs, que simplesmente tiram todo o trabalho dos processos de worker e o transferem para coisas rápidas sendo executadas na GPU. Mas, por outro lado, há o scheduler. O scheduler só precisa continuar sendo mais rápido do que esse trabalho. À medida que as GPUs ficam mais rápidas e a quantidade de trabalho na GPU diminui, a pressão sobre o componente do scheduler aumenta. Então, este é, na verdade, um alvo bastante adequado para uma reescrita em Rust
35:44 neste momento. Porque essa parte fica no host. Ela precisa manipular um pequeno número de recursos da GPU, mas não necessariamente precisa conhecer coisas sobre modelos e PyTorch. Então é uma reescrita mais enxuta, específica para cada engine e somente do lado do host. Assim, na trajetória futura dos inference engines, eu esperaria que esse fosse um grande alvo para torná-lo mais rápido, agora que a velocidade do desenvolvimento iterativo está diminuindo um pouco.
36:14 Certo, agora vamos olhar para dentro desses componentes, para ver o que está acontecendo neles. Vou me concentrar principalmente na parte do model forward pass. Vamos de dentro para fora. Na camada mais baixa, há kernels que executam operações como parte da arquitetura do modelo em geral. Em sua maioria, o engine delega esses kernels; ou seja, o engine não implementa todos os kernels para cada operação que precisa suportar. Ele tem
36:45 backends que implementam esses kernels, e os consome de bibliotecas externas de kernels. Esse é um dos motivos pelos quais não há muita diferenciação entre os engines em termos de desempenho bruto da GPU. A questão é, em grande parte, quão bem você consegue não atrapalhar a GPU. Há alguns kernels especiais por engine, como tree attention para speculative decoding baseado em árvores e no SGLang. Então, há pequenos trechos em que um engine tem um kernel específico, mas, se ele for útil e popular, acaba em alguma biblioteca de kernels, para poder ser usado
37:16 em muitos lugares; então, eles o consomem de uma biblioteca externa. Depois, os kernels são selecionados por esse código de model forward pass, que controla o fluxo e escolhe quais kernels executar. Em geral, isso é implementado em cada engine e para cada modelo. Se você quiser adicionar suporte a uma nova arquitetura de modelo, isso precisa ser implementado em cada engine. Em geral, há uma implementação de referência fornecida por uma biblioteca como transformers, mas ela é
37:47 insuficientemente otimizada, considerando a configuração. Então, acho que no SGLang e no vLLM agora há um caminho em que você pode simplesmente usar essa implementação de transformers, e eles talvez façam uma pequena intervenção para ver o que conseguem fundir automaticamente em kernels, otimizar automaticamente ou reescrever. Mas, em geral, para a maioria das coisas que as pessoas querem executar, você vai seguir esse caminho de selecionar manualmente quais kernels executar — tão manualmente quanto se escreve software hoje em dia. Depois, envolvendo esses model forward
38:19 passes, temos a construção do batch, que seleciona as entradas para esse model forward pass. Essa é uma das principais fontes de diferenciação especial de cada engine. É preciso configurar esse sistema de filas. Por exemplo, talvez você queira não operar filas separadas de prefill e decode, mas operar uma única fila em que possa misturar prefill e decode. Isso pode ter algumas implicações no desempenho. Então, talvez você também queira poder alternar dinamicamente entre executar apenas prefills,
38:52 apenas decodes ou uma mistura. E acho que, na operação ideal de um servidor de inference, não há filas. Então, como alguém que opera essas coisas, considero as filas um problema. Mas o engine deve ser capaz de lidar com isso. Talvez seja exagero dizer que filas são um problema, mas você não quer que isso vire uma fila crescente. Quer que ela seja esvaziada o mais rápido possível. Por fim, envolvendo tudo isso, temos a detokenização
39:23 e a tokenização. Isso é, em sua maior parte, algo pouco interessante, eu diria. Está resolvido nas bibliotecas de tokenização existentes, fora do engine. Acho que a exceção são as entradas e saídas multimodais. Agora temos modelos multimodais de entrada com pesos abertos bastante bons. Se você olhar alguns traces, verá que ainda há muitas oportunidades de otimização nesse caminho. Tokenizar uma imagem é simplesmente um problema mais difícil do que tokenizar uma
39:53 string de bytes Unicode, partindo do pressuposto de que você já tem um tokenizer. Ainda há mais trabalho a fazer. Por exemplo, talvez seja preciso usar o ffmpeg para extrair frames brutos e passá-los para algo que os transforme em um tensor. Então, sim, essa talvez seja a parte mais interessante. Uma maneira de conceituar a estrutura em torno do código de model forward é que esse código vai chamar algumas
40:24 bibliotecas de tipos diferentes ou escrever diretamente seu próprio código para executar na GPU. Boa parte passa pelo código do PyTorch, que tem suas próprias bibliotecas de kernels e consegue executar coisas na GPU. Depois, há bibliotecas de kernels específicas das quais você pode recorrer, que não fazem parte do engine. E, por fim, o engine tem seu próprio conjunto de kernels que podem executar código diretamente na GPU. Então, analisando isso um pouco mais, o
40:55 diferentes tipos de bibliotecas ou escreve diretamente seu próprio código para executar na GPU. Muito disso passa pelo código do PyTorch, que tem suas próprias bibliotecas de kernels e consegue executar operações na GPU. Depois, há bibliotecas de kernels específicas que você usaria, mas que não fazem parte do mecanismo. E, por fim, o mecanismo tem seu próprio conjunto de kernels que pode executar código diretamente na GPU. Então, para examinar isso um pouco mais de perto…
41:26 os kernels que existem nos backends deles. O núcleo das arquiteturas de modelagem de linguagem atuais se parece com isto. Há uma sequência de camadas: você recebe uma entrada, passa por ela, faz alguns cálculos entre tokens na sequência e, depois, faz alguns cálculos por token para enriquecer a sequência. Então, incorpora isso de volta à sua representação e repete o processo camada após camada. Isso se divide muito bem em duas classes de kernels realmente críticos e rápidos
41:59 disponíveis, especialmente por meio dos backends. O cálculo entre tokens é feito com attention ou linear attention. Há vários backends diferentes para attention. Os kernels do FlashAttention, do Tri Dao e de outros. O FlashInfer, da Nvidia. O CUTLASS, também da Nvidia. As implementações desses kernels em Triton. Essa é a biblioteca da OpenAI para criação de kernels. O TRT-LLM também separou seus kernels
42:30 e eles estão disponíveis como backends para attention. Acho que muitos dos mesmos nomes que aparecem como backends para attention também aparecem como backends para as multiplicações de matrizes dentro do cálculo por token, que é a camada MLP. É aí que estão os maiores ganhos. Em algumas arquiteturas, isso é uma MLP densa, uma rede neural clássica. Em outras, são essas misturas esparsas de especialistas, que basicamente dizem: não envie todos os
43:01 tokens para a mesma rede neural. Decida, para cada token, para qual rede neural menor enviá-lo. Para as MLPs densas, há uma biblioteca bacana da DeepSeek, a DeepGEMM, muito usada no SGLang como uma espécie de padrão. No vLLM, há alguns backends voltados para quantização, como o Marlin e o bitsandbytes. Para o grouped GEMM, você pode dividir isso em
43:32 primeiro, há esse problema de roteamento ou comunicação: para onde vão os tokens e como reúno tudo no final? Isso pode ser feito com um conjunto separado de kernels para essa comunicação all-to-all e esse roteamento, como o DeepEP, da DeepSeek. E isso pode ser separado da etapa de multiplicação de matrizes. Isso pode causar penalidades de desempenho; por isso, também é possível fazer tudo em um único kernel grande, um megakernel,
44:03 como é feito na arquitetura DeepSeek V4. Certo, então, com o vLLM e o SGLang, são coisas que você pode configurar e controlar. No SGLang, isso fica bem à vista na forma de configurações no início, com padrões inteligentes. O vLLM se concentra muito mais nos padrões inteligentes, e aí você altera as coisas usando variáveis de ambiente. Pelo menos da última vez que verifiquei, essas
44:33 coisas mudam a cada poucos dias. Mas, sim. >> A pergunta é: quem decide quanto? >> Sim. A pergunta era basicamente sobre o que acontece com a concorrência quando há um número muito grande de solicitações.
45:04 Isso nos leva a um nível muito mais alto do que o dos kernels e backends. Isso acontece no nível da construção de lotes e do escalonador. Basicamente, o escalonador vai fazer o melhor possível para decidir quais solicitações executar, geralmente dando prioridade às solicitações para as quais o KV cache já foi alocado, porque assim não é preciso recalcular. Se for necessário remover algo do cache para abrir espaço para outra solicitação,
45:35 isso geralmente vai para a memória da CPU e, depois, para o disco. E, voltando ao que eu disse antes sobre filas, considero isso essencialmente um estado de falha do engine. Se isso acontece, significa que implantei o engine de maneira inadequada. Se chego ao ponto em que o KV cache fica oscilando ou as coisas são gravadas no disco, como acontece com a memória de troca do sistema operacional — é possível transferir memória para o disco, mas, nesse ponto, seu MacBook fica inutilizável, certo? Então, é um recurso útil de
46:06 ter, mas quase como uma proteção para que o sistema não fique totalmente fora do ar quando houver um problema no serviço. Sim, como funciona no Modal? >> Sim. A pergunta era sobre deploys serverless e cold starts. Não vou falar muito sobre isso porque prometi ao Swix que não faria uma apresentação sobre um fornecedor. Mas, por favor, fale comigo
46:37 depois. Há uma publicação no blog chamada Truly Serverless GPUs que explica como resolvemos esse problema. Criação de snapshots de memória, snapshots da memória da GPU, um sistema de arquivos personalizado: várias peças para acelerar os cold starts. Sim. Legal. Certo. Vamos falar de algumas das principais técnicas necessárias para implementar dentro de um engine de inference e garantir um bom desempenho. A attention é fundamentalmente quadrática, pelo menos na parte mais importante da
47:08 attention em todas as arquiteturas populares. A solução é armazenar as coisas depois de calculá-las. Você faz uma pequena troca entre espaço e tempo: obtém complexidade computacional linear em troca de armazenamento linear. Isso cria problemas com a quantidade de dados que você consegue armazenar na memória da GPU, porque muitas vezes é mais lento armazenar em outro lugar e depois carregar de volta do que recalcular. GPUs são rápidas. Então, é preciso usar a memória da GPU
47:40 com eficiência. E, muitas vezes, há solicitações que compartilham prefixos. Por exemplo, todos esses mandamentos começam com “não farás”. Assim, é possível reutilizar o cálculo de tudo até aqui. Assim que um único token é diferente, já não é possível compartilhar o prefixo. Então, a estrutura fica mais ou menos como uma árvore ou um trie. Mas é importante conseguir aproveitar isso, especialmente se você tiver um system prompt enorme para um sistema de agent com dezenas de milhares de
48:10 tokens. É muito bom poder compartilhar isso entre usuários. A solução é o KV caching com páginas ou radices. Você armazena as informações em uma estrutura de dados de cache de páginas, como a que existiria em um sistema operacional, e depois precisa apenas reconstruir esses tensores sob demanda para passá-los aos kernels de attention. Antes, resolver esse problema era responsabilidade do engine. Agora isso foi incorporado aos kernels de attention
48:40 como o FlashAttention 4. Nossa equipe trabalhou nisso: tamanho de página um e uma forma de KV caching baseada em radix, que você pode ver à direita. Então, isso mudou um pouco. Incluí isso aqui porque costumava ser uma característica definidora do engine, mas agora faz parte dos kernels. Na verdade, o problema do engine é gerenciar a capacidade do KV cache e a disposição do KV cache. Outro problema que já mencionei
49:12 é que o escalonador pode se tornar um gargalo no lado do host. Quando você executa coisas na GPU, tanto nesse nível quanto dentro dos modelos, o ideal é que, a todo momento, algum kernel esteja rodando na GPU e que as coisas aconteçam na CPU para decidir o que deve rodar na GPU. Mas isso nunca deve bloquear o progresso. É muito fácil, porém, acabar criando alguma sincronização entre host e dispositivo. É preciso passar informações entre eles. A GPU não pode simplesmente enviar a resposta diretamente ao usuário. Não seria
49:43 muito boa nisso, mesmo que executasse um servidor HTTP. Então, é necessário fazer esse trabalho na CPU. Só é preciso garantir que ele não bloqueie. Uma das principais técnicas para isso é o CUDA Graphs. Você pega todos esses kernels executados na passagem para frente do modelo, registra cada um deles e os transforma em uma única estrutura de dados grande: um DAG de operações, que indica que este kernel é executado e altera os dados neste ponteiro. Depois que isso é feito, este kernel pode ser executado para alterar o que estava
50:13 presente nesse mesmo ponteiro e misturá-lo com dados de outro ponteiro. Então, esse grande DAG de operações em C é o seu CUDA graph. E isso permite que você deixe de lançar cada um desses kernels da GPU com um pouco de trabalho na CPU para decidir o que executar, como executar e qual ponteiro usar. Você transforma isso em apenas um trabalho no lado da CPU para decidir qual graph lançar; então, tudo é lançado a partir desse único graph, que é o que aparece neste trace do Peretto.
50:45 Então, isso permite que você relaxe bastante quanto ao que acontece no componente de passagem forward do modelo, pelo menos. Outro problema é que o decode é sequencial e, toda vez que você o executa, precisa carregar todos os parâmetros do modelo ou todos os parâmetros ativos em uma arquitetura ane, e precisa fazer isso repetidamente, por padrão, para cada token. Então, em nome dos seus usuários, você pode estar carregando um terabyte de dados
51:15 ou 100 gigabytes de dados por token e quer produzir um token a cada dois milissegundos. Então, agora você está falando de largura de banda de memória na escala de petabytes por segundo. Isso é bem difícil de alcançar. Uma das principais técnicas para resolver isso é o speculative decoding, em que você usa um modelo separado. O speculator prevê quais serão os próximos tokens e, em seguida, você os passa pelo modelo-alvo. Então, o modelo-alvo avalia todos eles em paralelo.
51:46 E, se você aplicar as técnicas de amostragem corretas, como rejection sampling, pode garantir que a saída do modelo-alvo seja a mesma que você obteria se o executasse sequencialmente, até a precisão numérica — a mesma saída —, mas agora você o executou em paralelo. De certa forma, você transforma seus decodes em pequenos prefills, o que é ótimo. Vou pular alguns detalhes do algoritmo. Escrevemos uma ótima publicação no blog sobre isso nesta última semana ou duas. O ponto principal
52:19 é que isso é muito importante porque, à medida que você melhora a qualidade do speculator e aumenta o número de tokens que ele consegue prever em sequência, obtém, na prática, um aumento linear na capacidade de processamento do decode. Muitas outras otimizações que você pode fazer no seu inference engine são um jogo de pequenos ganhos: um por cento, 5%, 10% de cada vez. E você precisa fazer uma engenharia de desempenho muito difícil para obter cada um desses pontos percentuais. Mas, por outro lado, você pode se sentar e treinar
52:51 um modelo speculator melhor e, em primeira aproximação, investir dados e computação nele para obter um aumento de velocidade de 2x, 4x ou 8x sobre o desempenho de referência. Essa se tornou uma das principais técnicas para permitir operar, por exemplo, a mil tokens por segundo em hardware Nvidia, inclusive com modelos bastante grandes, sem precisar recorrer a um acelerador novo, como um LPU ou Cabris.
53:21 Há muitas técnicas de paralelismo, mas, por questão de tempo, vou pular esta. Você precisa conseguir dividir o trabalho entre aceleradores. Muitas dessas técnicas dependem dos kernels subjacentes. Você precisa de bons kernels que consigam operar com esse paralelismo. Mas, dentro do engine, no código da passagem forward do modelo — escrito de forma específica para cada engine —, é preciso cuidar de todas as comunicações necessárias. E é nas comunicações
53:53 que surgem muitos dos problemas dessas técnicas de paralelismo. Certo. Vamos falar de observabilidade nos últimos minutos. Serviços de inference, como todos os serviços, terão alguns bugs. Há de tudo: desde bugs no nível da aplicação, causados por modelos que se comportam mal. Isso não é realmente um problema do engine, e sim do desenvolvedor da aplicação, mas às vezes aparece no seu dashboard. Há também bugs na qualidade do modelo. Esses são problemas do engine. Você está realmente fazendo sua
54:24 inference corretamente? Ao aplicar essa otimização de desempenho, você garantiu que ela não alterou o comportamento do modelo? Um dos casos mais comuns é na liberação de um modelo. Os tokenizers costumam ter pequenos bugs. Eles tanto tokenizam quanto aplicam templates de chat, e muitas vezes há alguns problemas aí. Eles costumam ser corrigidos algumas semanas após o lançamento do modelo, mas é algo com que você sempre precisa ficar atento. E há também bugs de desempenho ou problemas de desempenho do engine. Os piores são as regressões ao longo do tempo. Quando uma réplica fica em operação por um período prolongado, você pode observar degradação de desempenho
54:55 devido a problemas que só aparecem depois que ela está em funcionamento por bastante tempo. Além disso, especialmente em um ambiente de deploy na nuvem heterogêneo, você pode observar diferenças de desempenho entre réplicas que precisam ser investigadas. O objetivo, nesses casos, como em qualquer tipo de depuração em produção, é ter observabilidade. Se você registrar informações suficientes para depurar usando apenas esses logs, não precisa iniciar um servidor de desenvolvimento nem perder um monte de tempo extra. Isso abrange desde
55:25 questões no nível da aplicação até, acho, o foco nos problemas do engine e na qualidade do modelo. Você vai querer saber fazer bem evals de qualidade do modelo como parte do seu deploy. Então, deve testar o seu deploy real com scripts de avaliação e benchmark. Há scripts no SUAN, há scripts no VLM e há ferramentas externas de benchmark. Use essas ferramentas, calcule alguns números e registre traces em algum lugar. Registre o máximo possível de dados sobre eles para poder depurar depois. E, em produção, registre traces
55:57 e tente obter feedback dos usuários sobre a correção, vinculando-o às solicitações correspondentes. Para bugs de tokenizer, uma dica: registre os IDs dos tokens. Configure seu engine para que os IDs dos tokens também sejam registrados. E, por fim, quanto ao desempenho, bugs de desempenho podem aparecer em qualquer lugar. Então, quanto mais métricas você registrar, melhor — mais métricas do que você acha que precisa —, porque nunca se sabe qual correlação cruzada vai tornar o bug muito fácil de encontrar, em vez de muito difícil. Em vez de passar por esses exemplos, vou falar um pouco sobre
56:28 isto. Este é um dashboard de endpoint no Modal, e vou mostrar como você pode interpretá-lo. Eu estava operando aquele projeto de arte que mostrei no começo e acabei de iniciar três réplicas diferentes para ver o que aconteceria com o aumento da carga. Houve um pico de tráfego, que você pode ver aqui embaixo, e isso levou ao aumento do tempo até o primeiro token, à medida que as solicitações começavam a entrar na fila do lado do prefill e não entravam no prefill até que as coisas
56:58 terminassem. Isso também aparece na latência entre tokens: os decodes primeiro ficam um pouco mais lentos e depois muito mais lentos, com o aumento da fila e a contenção na GPU subjacente. Nesse caso, a solução, voltando à questão sobre deploys serverless, é detectar o aumento da carga e iniciar novas réplicas. Essas réplicas entram em operação e começam a assumir parte dessa carga de solicitações; então, a quantidade na fila diminui
57:31 até voltar ao nível de referência, e o congestionamento é resolvido. Acho que essa é a principal abordagem. Em muitos casos, aumentamos os recursos disponíveis para resolver congestionamentos e filas. Quando for preciso analisar mais a fundo, vou pular esta parte. Mas o NSI Systems ou o torch profiler são ferramentas essenciais. Eles permitem ver o engine inteiro de uma vez, todos os processos em execução e todo o trabalho que está acontecendo na GPU.
58:02 E identificar problemas como: “Ah, esta GPU está executando tudo mais devagar que as outras.” Acabou sendo um problema de reconhecimento de NUMA. Certo. Então, para encerrar, se quiser saber mais, mini-sglang e NanoVLM são ótimos pontos de partida. São implementações simples da arquitetura do mecanismo, feitas pelas duas equipes. São ótimas para pessoas. São incríveis para agents. Elas conseguem colocar todo o contexto, sabe, na context window. Alexa Gordic fez uma…
58:33 …ótima publicação de blog com um passo a passo sobre VLM. E vou compartilhar estes slides depois. Também trabalhei nisso em um notebook, para você poder experimentar; aí, você pode apontar seu agent de programação para a base de código e fazer perguntas. A Cognition AI também opera uma ferramenta chamada Deep Wiki, que basicamente percorre vários repos, incluindo SGLang e vLLM, cria bons diagramas de arquitetura e monta uma página no estilo wiki, onde você pode ler, fazer perguntas e depois perguntar ao agent deles, que…
59:04 …é uma boa combinação para usar com a sua própria configuração de agent. Então, é isso. Estou sem tempo. Vou apenas dizer rapidamente que, se você tem interesse em fazer o deploy e administrar sua própria inferência, temos um novo produto na Modal para ajudar você a começar a fazer o deploy com muita facilidade. Obtenha otim… obtenha uma linha de base inicial de um deploy de mecanismo de inferência razoavelmente otimizado, que você pode usar como ponto de partida com os endpoints da Modal. Então, confira isso. E, se tiver interesse em trabalhar com o deploy de inferência para…
59:35 …centenas de equipes, confira as vagas em modal.jobs. Obrigado. >> [aplausos] [música]
Transcrição original
[00:12] All right. Um, so I’m going to go ahead and get started while people get seated. Um, we got a pretty tight turnaround. A lot of really wonderful workshops at this event. Um, so I want to make sure we have plenty of time. Uh so I prepared for 10 or 15 minutes of questions and I think it’d be best especially given this is a workshop if people just interrupted me as I was going raise your hand and I’ll call on you and answer your question. Um and so just like let’s keep
[00:44] this interactive. Um I think it’s it’s better that way. Um, and it seems like talked to a couple of people, it seems like we got a lot of folks here who know their who know their inference, uh, know their engines, and so we can get a a sophisticated conversation going. Um, so the title of this talk is what is an inference engine anyway? And the goal is to sort of peel back some of the layers that are behind APIs and uh what is go actually going on inside of the
[01:16] code that runs your inference uh when you run it yourself. So roughly you know there tokens go in tokens come out um and a lot of the time you get to avoid thinking about what happens in between. Um there’s this really high quality open source software out there for running your uh running your language or multimodal model inference. You’ve got SGA, VLM, Tensor RTLM. Many people write similar things to these in-house. Uh they design these these uh
[01:48] engines that like at a high level just taking in some tokens putting out some tokens is not that hard. Um you can write something like this in using PyTorch and hugging face transformers in like an hour or two especially if you’re good with agents. Um but making it uh extremely high performance so that you can produce tokens with good tokconomics is the challenging part. Um and that d like sort of drives the design and architecture of these systems.
[02:19] Um so just as like a very quick sketch um before we jump back to the high level and talk about why why we’re here. Um the uh these are generally architected as a set of communicating processes. One that does IO does HTTP or RPC serving and responds to the outside world. Um and then uh there’s a little bit of pre and postprocessing with tokenizers and detokenizers. uh and this wraps uh the
[02:49] sort of core of the system where the real work gets done um in sort of two pieces. One a sort of scheduler that like defines and schedules work to run on the accelerator, usually a GPU. Um and then the actual code that runs on the GPU. Um and so this these two components in the bottom right here are where the sort of majority of the performance sensitive work gets done and where the complicated problems are.
[03:19] [snorts] Um so that’s where we’ll spend the majority of our time uh discussing. Um, all right. So, with that stage set, um, I think, uh, I’ve been to AI engineer since it was the summit and talked about, uh, running, uh, running inference, uh, every time. And the answer to this question gets better and better each time. Uh, the question of like why inference and why you should care about it, why you should think
[03:50] about being uh, an inference engineer who operates inference engines. um our uh fearless leader and uh uh one-man conference army Sean Wang uh tweeted recently uh that everybody in AI infrastructure the boring stuff uh like inference engines not the sexy research stuff um is finally getting filthy rich um and so what uh I think he’s pointing to here is there’s tremendous demand uh for these uh to run inference uh there’s
[04:22] demand at the labs there’s demand within companies who want to own their own stacks. There’s demand to set up and rethink the infrastructure stack and all of this wraps this core inference workload. Um but Sean is also pointing out that a lot of what people have been thinking about up to this point um is sort of the research side, the production of models. So um why not, you know, why not think about training? Why not go to the training engines uh talk with your precious time? I think the fundamental
[04:53] reason why this is such a critical place uh for engineering work to get done and why it’s such an like cool place to work as an engineer is that in the end training is a cost center and inference is a revenue center. Um so when you train models um it turns out um people have not generally been able to make businesses work where they sell model weights to people directly. Um and so you can’t really turn a training setup into like a proper self-supporting system. Uh but uh inference once you
[05:25] have a model um is something that people will pay for. People will pay for a system to you know take in their tokens and their context to produce new tokens, produce software for them, whatever. Um there’s a lot of opportunities to do inference. we have set up where there are a small number of essentially model foundaries who produce foundation models that are then deployed and customized by inference engineers. Um and then even for training inference has uh snuck its way in. A lot of the
[05:56] post-training adaptation of these foundation models requires that you generate samples from the model uh in inference and a lot of the same technologies come to play. A lot of the same concerns are present. Um, and then finally, sort of more selfishly as an engineer, there’s like right now inference is still very new, even though we’re years into this new technology. Um, and so you end up kind of straddling the whole stack from application architecture uh through linear algebra down to like electrons and hardware. So
[06:26] it’s a an engineer’s playground. Um, so we’ll see a lot of that as we go. Um I’ll maybe in the interest of time since we kind of had to get started very quickly I’ll skip over this but I’ve been working on inference for a couple of years mostly uh at modal um since coming from the sort of training world before that um and helping a lot of teams sort of deploy and optimize their inference on hundreds or thousands of GPUs on the modal platform. Um I’ve also been making a lot of uh inference applications myself. This is a little art piece that I made. Um, we uh uh had
[06:58] it in the uh Legion of Honor just this past week. Um, this art piece takes what it sees, passes it through a vision language model, one of the Quenes running on uh uh served via the SGA inference engine and running on modal. Um, it takes that input and it describes it as though it were an art piece. So, it produces one of these little museum placard uh type things. And as everybody knows, you’re not art until somebody writes one of these little placards to describe what you’ve done. Um, so I pointed it I pointed it at the line on
[07:28] the way in. Um, and it came up with a cute little description. Anyway, so this is the kind of, you know, uh, sort of in the middle of the set of inference applications between the lowest latency and the uh, highest throughput applications that get deployed. Um so uh I’ll start by talking in more detail about those workloads like what workloads land on these inference engines and then jump quickly jump move from that quickly into architecture how these engines are designed some of the key techniques uh that fit within that
[07:58] architecture to make their uh improve their performance. Um and then lastly, a key piece for making these things work in production, observability and uh like debugability of these engines for correctness and for performance. All right, so let’s start with workloads really quick. Um uh I like to break things down into kind of three pieces. There’s chatbot plus, which is like the original killer app of inference, something like chat gypt or clawed code. there’s a person waiting on the other side interacting with this inference
[08:30] system and providing additional context. Um the plus uh indicates that not only does it talk to a person, it also probably interacts with external systems via tool calls. Um then you have background agents. They often do similar tasks to something like clawed code. Um but they do it in the background, not directly in response to a person. So something like Devon, Ramp inspect, uh OpenClaw or Herbies for a non-coding case of a background agent. Um so uh this is uh characterized by a similar sort of affordances or workloads but a
[09:03] different performance profile. And then lastly data processors. Um these generally run relatively small models. They take unstructured data like a PDF and they turn it into structured data. Um they extract some information from it. Um usually the sort of thing you might insert in a database. This is something like the uh reductto document processing platform or something like Fathom that takes a a video and extracts the transcript. Um okay so under the hood what do these
[09:34] uh workloads look like? Um you send in some tokens from a client uh that goes to some server somewhere. Um for now we’re going to focus on cloud deployment of inference because that’s where the majority of it happens right now. Obviously in the future a lot of these inference engines are going to end up running on people’s uh machines like in a couple of years. Um yeah, go ahead. >> The size of the screen >> brightness of the screen. Um yeah, so
[10:04] the question was about adjusting the brightness. I’m not sure if we can uh fix that. That’s what I get for making dark mode slides, but yeah. Um yeah. All right. So a client submits um an input uh and then it’s processed by our engine running on a server somewhere. Um first we process the input tokens in the prefill phase and then we generate the output tokens in the decode phase. Um, and so we have uh one uh one prefill uh run uh does a forward pass through the
[10:36] model and then multiple decode runs to produce one or a few tokens at a time and then once we’re done return them to the user or generate a tool call and interact with an external system. Um so with these work these both the external interface and this sort of internal pre-filled decode split defines these uh the sort of metrics that you care about for a workload that sort of tells you how to deploy your inference engine and and how to measure its success and performance. So you have
[11:07] something like queries per second in and queries per second produced. Um this one pretty tricky to think about uh under control of your users. um uh you probably have a better time thinking of it uh at the engine level thinking of how many queries per second you can handle per replica of your inference engine. Um and then you have aggregate demand coming from users. Uh then with those queries you want to look at how many tokens there are per query um input and output aka prefill and decode. Um so
[11:38] this one is tricky because it’s not only under control of users but it’s also under the control of a model. Uh so the determination of when to finish processing a query is not under your deterministic control. It’s under the control of the model inside the inference engine. Um so this is something not both of those sources of nondeterminism means you need to kind of like measure this benchmark it to get it. Um and then uh when we do our processing we uh we produce some intermediate artifacts that we want to cache in the KV cache. Uh and then the
[12:09] degree to which that cache is reusable is a key component of the definition of our workload. Um this is also mostly under control of users but you can get like depending on the workload there’s actually like pretty uh relatively fixed patterns of of prefix reuse. Um uh and then finally coming from the application layer um you have some uh latency budget some SLO of like how quickly you want to return tokens. That could be how quickly you finish something like the prefill
[12:39] phase and have your first tokens ready to send to users. Or it can be how quickly you can generate each set of tokens during your decodes and get them back to users. So that’s your time to first token and your time per output token or inter token latency. Uh so this is going to be the like primary constraint that you operate under. Okay. Um, and we can map those back onto our application archetypes. Uh, like chatbot plus background agent, really high prefix reuse. There’s some interactivity going on whether that’s a person or an
[13:10] agent. Um, the chatbot plus generally has relatively short decodes, relatively short outputs of tens to hundreds of tokens. Um, and a very tight latency tolerance on that. Like a person uh wants to see something in a few hundred milliseconds at the very most, or they get bored, uh, they get angry, they turn away. Um, with a background agent, you maybe have a few minutes to produce a PR, right? Um, if it’s as high quality as an engineer, maybe as like what would be produced by a good software engineer, maybe you have a few hours. Um, and so there’s not as tight of a of a budget on
[13:40] your milliseconds of network overhead or whatever. Um, and then with a data processor, you generally have very low prefix reuse. Um, you are seeing different documents each time. There’s maybe a system prompt, but it’s generally small relative to the other inputs. Um, and then you have a very short decode. you’re producing some structured object that’s generally short relative to the input. Um the structured version is shorter than the unstructured version. Um and then the demand here is less on latency generally and more on aggregate throughput because you’re
[14:11] generally like ripping through a database backfill type job in a lot of these. All right. Um and so maybe just want to call out uh like now that we’ve looked through this you could see that like even though at the application layer this feels like kind of one it’s one single workload from the perspective of the user once you start drilling down into what the engine is doing it really feels like there’s two subworkloads going on. there’s the like workload to do your prefills to do the processing of
[14:42] input prompts. Um, and then there’s work to do the decodes to produce the outputs. Um, and these uh they sort of operate semi-independently inside the engine. Um, and so need like a sort of like different perspective and they follow often like different code paths. Um, and they’re sort of defined by the fact that the per user in the decode phase, you do you’re doing far less arithmetic, far less math per uh per decode per user than you would do in the
[15:13] prefill phase, which is a lot like much higher arithmetic intensity, a lot more uh uh a lot more math per request. Um, and so that’s your fundamental constraint, but also the sort of space in which you operate in configuring an engine and then like adding features or um, adjusting the engine behavior to serve these two things. Okay, so I wanted to make sure we’re all on the same page about some of the core stuff about what these engines are actually doing before we peel open uh, the insides and start looking at them.
[15:44] So what is the architecture of an inference engine? Um so um this is a little little diagram showing at a high level what an inference engine does. So the goal of an inference server is to take an HTTP request or a gRPC request and turn it into an HTTP response to the client. Um and so it does that by passing through all these things down here. Um so the idea in this diagram um is that if you follow any set of arrows you get the same thing. So uh you can
[16:16] sort of peel back a layer of the arrows and look inside and say all right so what what is the the inference server does this high level thing what’s the next level down the inference engine is the sort of like interesting part of it is the is the part that is not doing the HTTP serving because we aren’t in one of those situations where HTTP serving is the hard part is your bottleneck it’s not like a proxy um it’s uh it’s a this is a case where you’re handling tens hundreds maybe thousands at most of requests and responses per replica and
[16:47] so general purpose hardware just you know kind of eats this for lunch. Um so the real interesting stuff is all at the layer below this. Um so at the top is your like your server IO for HTTP requests. At the bottom is the actual engine and it’s like collection of processes. Um and you can kind of break this out into three pieces. The request comes in expressed using like sort of human friendly um or external representations. So you have Unicode strings, you have PNG images, you have MP4 videos or WebRTC streams. Um, and
[17:19] these need to be pre-processed before they can get passed to the actual inference. So they get turned into collections of tokens. um which then get mapped into tensors on the GPU and then we can actually do the inference part of the inference engine which takes you know in the simplest case it takes a tensor with K entries in it and gives you a tensor with K plus one entries in it and these are machine learning models that predict the next token they guess what would the next entry in this big tensor look like um based off of the
[17:50] data I’ve seen in training whether that’s data from the internet or data from what a helpful agent would do or RL data um whatever that you learn to predict this next tensor entry. So that’s the core right there. Um and that is what is wrapped by the rest of the inference engine. Um so then we have post-processing on the side here. So post-processing turns that new tensor into a response. Uh and that’s generally done by this detokenizer that goes back to the sort of like token space first and then uh and then back to our um back
[18:23] to the human and external system friendly representation in the response so it can be returned to the user. question. >> Could you explain the difference between the inference server and the inference engine and where does >> Great question. Yeah. So the question was what is the difference between an inference server and an inference engine and where does something like VLM lie? Um so inference server uh means that
[18:53] you’ve wrapped some like input output around the core operations of like taking requests and turning them into responses. Uh so VLM has both of these like VLM operates as a an HTTP or gRPC server. Um and then you the engine layer is sort of like part of the VLM or suing SDK as like VLM. Um so most deployments are look like are deployed as a server with like a single
[19:25] request single response kind of interface um as depicted here. Um yeah, does that answer your question? Yeah. Um cool. Um I said this earlier but some people have come in since like please interrupt raise your hand with questions if I’m like if there’s anything confusing or if you’d want to dive deeper. Yeah. >> Rom. >> Yeah. So the question from Rustom is is this like a stateful or stateless uh
[19:55] workload? So the answer is it is stateless [snorts] if you don’t care about performance. Um it’s stateful once you do care about performance which is pretty much every case. So and the primary state that you accumulate over time for requests is the KV cache of like previously computed things for for a previous request attached to some notion of session. um and that handling things like sessions actually lives outside of the inference server/inference engine in general. So
[20:27] the an individual engine replica can track this KV cache but is up to the wrapping layer which might be something like Nvidia Dynamo or LMD to handle like defining what a session means. So it’s like the clients in that architecture sort of define the the sessions. Um and that is where a lot of the trickiness comes in. Um, so we won’t talk too much about that because it’s kind of outside the there’s enough to talk about with engines um already, but yeah, uh, come find me afterwards um, if you want to talk about that layer.
[20:58] Um, right. So this diagram, we also talked about it previously, but I just want to return to it now that we’ve seen a little bit more. So this outer layer is our server IO process. This um, is this is specifically SGA’s architecture. There’s some slight differences with VLM, but this server IO process communicates with the outside world and then with our pre-processing and post-processing. Those things communicate with auler process um which is the one that defines and schedules work onto the GPU, handles things like queuing. Um and then the not ready for
[21:32] that yet. Um, and then the one or more processes that are sort of closely tied to the GPU are the ones that do the real work of doing the inference. Um, so the yeah, so the key things to pull out here is that if you are an old school ML person who’s been in this world for a long time, the model forward passes layer is the one that you’re familiar with. This is where the vast majority of the sort of like PyTorch uh and similar code shows up. Um and this is the pl like the GPU or the
[22:05] accelerator in general is the component that costs the most um and is capable of the most work like paflop per second scale operations. So this is where the like hardcore shave a shave a microcond performance engineering is focused. Uh but critically thisuler process out here or it’s equivalent in another architecture is your is the bottleneck to that component. Um and so this is it’s generally operates um it is
[22:36] literally singlethreaded in implementations we’ll talk about but is also like semantically it’s single threaded. It’s this one thing that is defining somebody has to define the work that happens on the on the GPU. somebody has to manage a bunch of resources there and there’s some some fiddly like tricky things to do concurrently. And so this becomes this uh like semantically singlethreaded uh like point of control that can potentially despite the fact that it’s not doing much it’s not doing as many
[23:06] operations uh as the work on the GPU it actually becomes your bottleneck. It’s the sort of gatekeeper to the GPU. And so this is the other place where the majority of like performance engineering and care needs to go in the design and operation of an inference engine. Um so we’ll see more on that as we go but but that’s like the high level takeaway about these things. >> So is the solution to that bottleneck
[23:36] problem? Uh yeah so the question was is the solution to that bottleneck to you know operate the schedulers in parallel or um uh or to improve some of the external components that consume from it. I think the current state of affairs is that this is not reached the like that you can operate these server processes in Python and you just have to be faster on the host side than the GPU side and
[24:08] there’s not the like complexity increase that you get from trying to manage like locks or coordination on the GPU resources is much that complex lexity increase is much greater than the gain um because even if you were able to get it down to a microscond the work wouldn’t start happening until the previous work on the GPU had finished and that’s you know depends on the particular workload but you’re talking generally tens of milliseconds to hundreds of milliseconds and so the like um yeah
[24:42] there are general most of the time you can get around it we we’ll talk a little bit more about this so maybe ask again once we get to if if that wasn’t satisfying. Um but yeah, great question. More good questions, please. Okay. Um so then just looking at that was like the architecture diagram that you think of as your like engineering. Um let’s look at the like life of a request in this architecture. So the um the server like receives an input from the outside world. It says okay, we’ve got a request. um it sends it to the tokenizer
[25:14] to prepare or pre-process this request that then goes into the scheduler that decides like okay how many resources is this thing going to consume what resources are available um and then that runs this core loop of creating um batches of requests to go in the model runners. So generally most of the accelerators like benefit from operating on multiple requests in parallel. Um so especially GPUs but also TPUs and and many others. So you want to collect a
[25:45] bunch of requests together and run them. Um if you’re a database person you might imagine like I’m g I’m about to run a sequential scan so I might as well grab like three or four queries that are all going to like read this entire table and do all of their um like you know do all their queries at once. Um because the hard part is that like sequential scan of loading the entire table. Um so there’s a similar thing going on here. Um uh so this core loop operates over and over again start a batch and outputs the like uh the tokens and the
[26:15] probability distribution over tokens the the logits. Um that comes from the the model runner or runners. Um so that operates um sort of continually. Uh and then as tokens are ready uh they can be passed to the detokenizer to be like turned back into a proper response to the outside world and then sent back to the server. So yeah, this is what’s interesting about this stuff is that it is simple relative to what’s going on inside
[26:46] databases, file systems, uh like web servers. Um, partly because it’s fairly new, but partly because there’s just this core bit at the middle that you just want to operate at maximum speed. Yeah. Question. >> The question was, is there communication between the tokenizer and the detokenizer? Um, to my knowledge, there is no direct communication between the tokenizer and the detokenizer in SG lang. Um, in VLM, they actually live inside the same
[27:17] process. the um manager, the engine manager. And so like it’ll be a little harder to determine if there’s any communication there. Um but in general, they actually don’t need to communicate with each other. They just need to operate essentially the same um system for mapping from like inputs to uh tokens or tokens to outputs. Yeah. Did that answer your question? >> Yeah.
[27:47] I’m not sure >> Right. So the the question was about basically maintaining coherence between the tokenizer and the detokenizer and how would you handle maybe like ambiguous um like ambiguous inputs. Um the answer is that yeah they they run
[28:19] the same like underlying li like underlying libraries like the usually like the tokenizers library from hugging face. Um and the I would say the tokenization part is the one that is harder than the detokenization part. the detoization part is like a little bit more entirely under under the control of the person implementing the the detokenizer. Um yeah. Um >> software
[28:50] >> yes. So the question was does the software allow the tokenizer and detokenizer to always make the same decisions? I would say yes. I’m trying I’m like trying to make sure that I’m not overpromising here. Um but I don’t think you need communication between them to ensure that the same decisions are made. Yeah. Question. Sorry, the person. Yeah. >> Yes.
[29:21] >> Right. So first pointing out that the tokenizer can be um parallelized and generally is like multiple processes um and yet you still see longer time to first token uh if you have longer input sequences. So why is that? Um so the answer is that processing a longer sequence of tokens takes longer inside of this core loop. Um so it’s the like primary driver. Um if you have a 100,000 tokens that will take let’s call it 10
[29:51] times 10,000 tokens. Um once you get to a certain uh size it’s basically linear in the input sequence size. Um then in a like running inference engine there’s also effects from like you break up the input tokens into multiple pieces and run them as separate batches. So you can also get additional if there’s like queuing and congestion that adds more to the um uh processing of a uh of a long batch um than to a short batch. You experience the queueing delay multiple times possibly. Um but maybe the one
[30:23] underlying um point to clarify like producing the first token means producing the first output token not the tokenization process. Um and then finally not a part of the question but something I wanted to point out. It is interesting that you generally have multiple deto tokenizer processes um and one dtokenizer in the sgang architecture at least. Um and the answer there is that input tokens are come in at like a much higher rate than output tokens are produced. Um and that’s related to the fact that prefill can operate at much
[30:55] like higher rates of total tokens per second than decode can in general. Um, and so you there isn’t as much pressure on the detokenizer component of the system to operate like at the as quickly. Um, and it’s also in some it’s bottlenecking your response to the user, but it’s not bottlenecking the core model forward pass resource. So it’s also like off the hot path a little bit. Um, yeah.
[31:27] >> Yeah. So the question was how much of the latency comes from this part in the tokenizer versus this part in the core loop. This is going to depend on your workload. Um I would say there was a nice thing from uh who was that Cruso I think um that pointed out that the existing tokenizers generally don’t show up as a bottleneck if you have models that are let’s call it like 20 billion parameters total or or above. um and that and if you but if
[32:00] you are operating a smaller model and you’re operating on very large input contexts then it can show up as a as a bottleneck. So that’s maybe a good rough guide. I would say like I actually have not had to think about the tokenizer latency very much. It’s generally been prefills and substantially decodes that are your the like latency bottleneck. And so decodes if you’re producing 1,000 output tokens a second, each one is like one millisecond on average. Um and the um but up to like 10 milliseconds, 20
[32:32] milliseconds um or even more if you have a um lower decode throughput like a large that you’re running at lower interactivity. And so that very the tokenizers are definitely millisecond scale or less and only happens once um per request. Yeah. Also notable that nobody talks about tokenizer caching. People talk about KV caching but not tokenizer caching even though you could do it. Um I’m going to keep going just to make sure we get through more stuff but these have been great questions and so if it’s still relevant please ask. Um
[33:04] another important question to ask wait why are we doing multipprocessing? The key reason is they want we want separate threads of control for each subcomponent for our server IO for our tokenization and detokenization for each model runner. Um we want them to be able to sort of operate as independently of each other as possible. Um they are many of them the tokenizer and the detokenizer are doing relatively computensive work relative to most of what happens maybe in a in a in a web server. Um the model runner is um like interfacing with the
[33:37] GPU or accelerator. It interacts with that in a way that’s kind of similar to the way you would interact with a nick um in like a web server, which is to say you offload work onto it. Um and uh so that one you you’re giving it its own event loop. Maybe in theory that could be part of like a broader event loop. Um uh but it’s uh certainly like cleaner to have these separate threads of control and optimize their uh you know optimize their latencies and there’s not sufficient uh interprocess communication to require them to like say live in the
[34:08] same thread. Um but why not threads uh for each of these instead of processes um and one of the fundamental reasons is that Python is single threaded per process. This has recently started to change. there’s still a lot of work for compatibility and performance to be done there. Um so that uh because Python has a global interpreter lock for all the threads in the same process, you would like frequently block on that. Um so it’s so that is another reason to sort of split these up. Um but that just uh begs the question um uh or I guess
[34:40] begets the question why Python um and the answer is Python has excellent model or GPU support. Uh so it’s like easy to run code on accelerators with Python. So specifically for that model um uh model for pass component um and so wait but why wait why does Python have excellent support here? Partly it’s that it’s a great language for researchers because it’s like ease of use and these uh models are still coming out of a sort of researchy data sciency environment. But there’s a deeper reason which is that Python has really has kept like really
[35:13] deep C and C++ interop as one of its core design principles and that allows for like much easier communication uh with these accelerators and uh with like accelerated code in general um than uh like a maybe equally ergonomic language like JavaScript. Um but why not like rewrite it in Rust? Um the model worker processes again they’re just trying to like tell the GPU what to do. So you could like the code on the GPU is written in a compiled language and and like carefully optimized like to within
[35:44] an inch of its life. Um but the model worker processes are just organizing that and there’s a few tricks like CUDA graphs um that like really just pull all the work out of the worker processes and into sort of fast stuff running on the GPU. Um but there is theuler on the other hand. So theuler just needs to stay faster than that work. Um, but as GPUs get faster and as the amount of work on the GPU goes down, the pressure on theuler component goes up. Um, so this is actually like a pretty good target for a rewrite it and rust type
[36:14] moment. Um, because this part is host side. It has to manipulate a small number of GPU resources, but it doesn’t necessarily have to know stuff about models and pietorrch. Um, and so it’s like a thinner rewrite. It’s like specific per engine. um and uh uh and host side only. So like in the sort of arc of future inference engines, I’d expect it to be a big target um making that faster now that the the sort of iterative development speed is slowing down a bit.
[36:45] Okay, so now let’s look inside of the like what what is going on inside of these components. Um um mostly mostly focused on the um the model forward pass piece. So at the very we’re going sort of like inside out. So at the very bottom there are kernels that run operations for as part of the model architecture in general. Um these are mostly deferred by the engine which is to say like the engine doesn’t implement all of the kernels for every operation it needs to support. Um it has
[37:16] backends that implement the uh these kernels and it consumes them from external libraries of kernels. This is one reason why there isn’t much differentiation between the engines on sort of raw GPU performance. Um, it’s mostly about how good are you at getting out of the way of the GPU. Um, there are a few special kernels per engine like tree attention for treebased speculators and SG lang. Um, and so there’s like little bits where an engine will have a specific kernel, but like if it’s useful, if it’s popular, it ends up in some kernel libraries so it can be used
[37:47] in lots of places and then they consume it from an external library. Um then uh the kernels are selected by this sort of model forward pass code that does control flow of and sort of like selection of which kernels to operate. Um and this is this is implemented in each engine in general and it’s implemented per model like if you want to add support for a new model architecture this needs to be implemented in each engine. um there’s generally a reference implementation that comes from a library like transformers um but that is
[38:19] insufficiently optimized um the like the setup there. So there’s um I guess in suang and vlm there is now a path where you can just use that transformers implementation and they’ll maybe do a little bit tiny bit of surgery to see what they can automatically kernel fuse or automatically optimize or rewrite. Um but in general for most things people want to run you’re going to go through this hand handwritten as handwritten as software is these days uh path for um like selecting which kernels to run. Um then uh wrapping those model forward
[38:52] passes is our batch construction to select the inputs to that model forward pass. Um this is kind of one of the primary sources of special sauce per engine. you need to set up this um this sort of like queuing setup. Um so you might want to be able to for example not operate separate prefill and decode cues but operate one sort of queue where you can mix prefill and decodes together. Um that might have some performance implications. So you might also want to be able to like dynamically switch whether you’re running pure prefills,
[39:23] pure decodes or mixes. Um and then you want to do this I think in the ideal operation of an inference server there is no queuing. Um, so as somebody who operates these things, I like I treat queuing as a problem. Um, but the engine should be able to kind of do it. Um, or may maybe it’s too extreme to say that queuing is a problem, but um, you don’t want this to be a like yeah a growing queue. Um, you want it to be flushed as quickly as possible. Um, and then lastly wrapping that you have uh detokenization
[39:53] and tokenization. Um, this is like mostly boring I’ll say. um it’s like solved in the existing tokenization libraries outside of the engine. I think the exception to this being multimodal inputs and outputs um we now have pretty good open weights multimodal input models. Um and if you take a look at some traces you’ll see that there’s like still many operate opportunities for optimization on this path. Um, tokenization of an image is just like a harder problem than tokenization of a um
[40:24] of a string of Unicode bytes like conditioned on already having a a tokenizer. There’s more more work to be done. You might need like ffmpeg for instance to like pull out raw frames to go in to something that turns it into a tensor. Um, and so yeah, so that is maybe the most interesting part there. Um the one way to sort of conceptualize the structure around the model forward codes is that the model forward code is um like going to call into a couple of
[40:55] different types of libraries or directly sort of write its own code to run on the GPU. Um so a lot of it goes through like PyTorch code which has its own kernel libraries and and can run things on the GPU. Um then there are like specific kernel libraries that you would draw from that aren’t in the engine. And then finally the engine has its own uh set of like kernels that can directly um you know run code on the GPU. Um so picking that apart a little bit the um
[41:26] uh the kernels that exist in their backends. The core of language modeling architectures these days look something like this. Um there’s a sequence of layers uh that you take an input, you pass it, do some cross token computation on the sequence, then you do some per token computation to enrich the sequence. Um and then you merge that back in with your representation and you iterate this layer after layer. Um and so this splits nicely into the two kind of classes of like really critical fast
[41:59] kernels that are available especially via the backends. Um so the cross token computation is your attention or linear attention. Um there’s a bunch of different backends for attention. The flash attention kernels from tree and jaw and others. Uh flash and fur from Nvidia. Uh cutless also from Nvidia. Um the Triton uh implementations of these kernels. That’s OpenAI’s kernel authoring library. Um, the TRTLM, the engine has also split out its kernels
[42:30] and they’re available um, as as backends for attention. I think you’ll see many of the same names that show up as attention backends also show up as backends for your matrix multiplications inside of your um, your uh, per token computation which is your MLP layer. It’s where the biggest uh, gems are. Uh it’s also in some architectures it’s this is a dense MLP a classic neural network. In other architectures it’s these sparse mixture of experts which basically just says don’t send every
[43:01] token to the same neural network. Per token decide which smaller neural network to send it to. Um so the um the dense MLPs there’s a there’s a nice uh library for this from uh deepseek deep gem that gets used a lot in sglang as kind of like the default. Um in VLM there’s some quantization focused backends for this the Marlin and bits and bytes backends on the uh for the group gem you can split this out into
[43:32] first like there we have this like routing or communication problem of where do tokens go and how do I bring stuff together at the end. Um so that can be done with a separate set of kernels like the uh for this all to all communication and routing like the another one from deepseek DP. Um and that could be sort of factored out from the like matrix multiplication step. Um that had that can have performance penalties and so you can also do that as like one big kernel um a mega kernel uh
[44:03] which is what is done in the deepseek v4 architecture. Um okay so these uh with VLM and SGA lang these are things that you can sort of set and control in SG lang it’s like kind of pushed up into your face in the form of configuration at start with smart defaults VLM is a lot more focused on the like smart defaults side and then you sort of like patch change things more with um environment variables. Um, at least last I checked, you know, these
[44:33] things change every like few days. Um, but yes, >> questions are like who decides how much? >> Yeah. So the question was basically about what’s happening with concurrency with really large numbers of requests.
[45:04] Um so that goes back so that’s uh much higher level than the kernels and backends. Um that’s happening sort of at our like batch construction and scheduler level. So basically the scheduler is going to do what the best that it can to decide like um which requests to run emp generally emphasizing requests that already have KV cache allocated to them um because then you don’t need to recomputee um the if you need to evict something from the cache to make space for another request
[45:35] um yeah that will generally get like put out to CPU memory and then disk um and I would say Yeah, to my point earlier about queuing, I think of that as essentially like a failure state of the engine. Um, that we like I’ve deployed my engine improperly. If I get to the case where I have KV cache thrashing or like things being flushed to disk in the same way that there’s like swap memory in your operating system, like you can swap memory to disk, but at that point your MacBook becomes unusable, right? Um, so it’s it’s a useful feature to
[46:06] have, but almost like um so that you don’t go hard down when you have a serving problem. Um yeah, how is mobile? >> Yeah. So the question was about like serverless deployments and cold starts. I’m not going to talk about that this much because I promised Swix I wouldn’t do a vendor talk. Um but please talk to
[46:37] me afterwards. There’s a blog post called truly serverless GPUs that talks about how we solve that problem. Memory snapshotting, GPU memory snapshotting, custom file system, bunch of pieces to make the cold starts faster. Um, yes. Um, cool. All right. So, let’s talk about some of the key techniques that you need uh to implement to in inside an inference engine to make sure like uh your performance is good. Um so attention is fundamentally quadratic at least like the uh loadbearing part of
[47:08] the attention in in every architecture that’s popular. Um and the um so the solution to that is to store things um after you’ve computed them and you do like a little space-time trade-off for linear computational complexity in exchange for linear storage. um that runs into problems with how much can you store in GPU memory because it’s actually often slower to store this somewhere else uh and then load it back in as opposed to recomputing it. GPUs are fast. Um so you want to be efficient
[47:40] with your use of GPU memory. Um and then you often have requests that share prefixes. So for example like all these commandments start with thou shalt not. Um, and so you can reuse the computation of everywhere up to here. Um, as soon as a single token differs, you no longer can share the prefix. So it’s kind of this like tree or try shaped structure. Um, but you want to be able to make use of that, especially if you have like a giant system prompt for like an agent system that’s tens of thousands of
[48:10] tokens. Um, it’s like quite nice to be able to share that across users. Um and so the solution to this is like KV caching with pages or or radices. Uh so you store the information in this um in like a a page cache data structure like you’d have in an operating system and then you just need to reconstruct these tensors sort of on the fly to pass them to attention kernels. So that that job is now it used to be the job of the engine to solve that problem. That’s now incorporated into the attention kernels
[48:40] like flash attention 4. something that our team worked on the um page size one um and sort of like RAD XC form of KV caching you can see on the right um and uh yeah so this is this has moved a little bit I put this in here because it used to be like a defining feature of the engine but now it’s kind of actually part of the kernels and really it’s the management of KV cache capacity and um and the like layout of the KB cache that’s the problem of the engine um another problem I already talked
[49:12] about how this the scheduler can be this um host side bottleneck. What you want when you’re running stuff on the GPU like both at the sort of level and then inside of models is that you want that just like at every moment some kernel is running on the GPU and things happen on the CPU to decide what that uh should run on the GPU. But they shouldn’t ever block uh progress. But it’s very easy to accidentally write some kind of host device sync. There does have to be information passed between them. the GPU can’t just like send the response directly to the user. Um, it wouldn’t be
[49:43] that good at it if it did run an HTTP server. Um, and so you you do need this CPU work. You just want to make sure it doesn’t block. Um, one of the key techniques for this is CUDA capture. So you take all of these kernels that get run in the forward pass of the model and you track all of them and you turn that into one big data structure a DAG of operations of like this kernel runs and and like you know mutates the data at this pointer. Once that is done this kernel can now run to mutate what was
[50:13] present at that same pointer and mix it with data from another pointer. So this big DAG of C of of uh operations is your CUDA graph. And this um allows you to go from launching like each one of these GPU kernels having a little bit of CPU work to decide what to run, how to run it, uh what you know which pointer to put um you turn it into just one CPU side work to decide which graph to launch and then all of it gets launched from that single graph which is what’s depicted in this um uh peretto trace. Uh
[50:45] so this allows you to relax quite a bit about what is going on in the model uh forward pass component at least. Um another problem decode here is sequential um and every time you run it you have to load like either all of the parameters of the model or all the active parameters in ane architecture um and you need to do that over and over again um by default for each token. So you’re like on behalf of your users, you might be loading like a terabyte of data
[51:15] or 100 gigabytes of data per token and you want to produce a token every couple milliseconds. Um so now you’re looking at that’s pabyte per second scale uh memory bandwidth. That’s uh pretty hard to achieve. Um so one solution is key technique for solving this is speculative decoding where you take um a separate model. The speculator guess what the next couple of tokens will be and then pass it through the target model. Um and then the target model sort of grades all of those in parallel. Um
[51:46] and if you apply the right sampling techniques, rejection sampling, um then you can uh guarantee that uh the output from the target model is the same as you would have gotten if you’d run it sequentially up to numeric um the same output uh but you’ve now run it in parallel. You sort of turn your decodes into tiny prefills which is nice. Um and so the um I’ll skip over a little any details about the algorithm. There’s a great we wrote a blog post about this uh just this past week or two. The key
[52:19] reason why this is really important is that as you improve the quality of the speculator, as you improve the number of tokens it can guess in a row, um you actually get basically a linear speed up in your decode throughput. So like many other optimizations that you can do on your inference engine are like the it’s like a game of inches of like a percent 5% 10% like each time and you have to do the like very like difficult performance engineering for each of those you know percentage points. Um but then on the other hand you could sit down and train
[52:51] a better speculator model um and to first approximation throw data and compute at it um and then get a 2x speed up or a 4x speed up or an 8x speed up um on top of the the the baseline. Um so this is has emerged as one of the like key techniques to allow you to operate say like a thousand tokens per second on um on Nvidia hardware without have for actually fairly large models uh without having to go to um like a a novel
[53:21] accelerator like an LPU or Cabris. Um I’m actually going to there’s a lot of parallelism techniques but in the interest of time I’m going to kind of skip over this one. um you want to be able to split work onto accelerators. Um a lot of this comes from the underlying kernels. You need good kernels that can operate with this parallelism. But within the engine in the sort of model forward passcode which is written on a sort of per engine basis um you need to do all of the communications around that. Um and so the communications is
[53:53] where a lot of the problems come in in these uh parallelism uh techniques. Um all right. So, uh, observability with our last couple minutes here. Um, inference services like all services will have some bugs in them. Um, you have everything from application level bugs from models misbehaving. Um, uh, which is really not the engine’s problem. It’s the app developer’s problem, but they’ll show up on your dashboard sometimes. Uh, model quality bugs. These are the problem of the engine. Are you actually doing your
[54:24] inference correctly? When you applied this performance optimization, did you actually guarantee it didn’t change model behavior? Um, one of the most common ones here is actually on model release. Tokenizers are often slightly bugged. They both tokenize and apply chat templates and there’s often some like uh some issues there. Um, they’re beaten down in a couple weeks after model release, but it’s something you always have to be like on guard for. Um, and then performance bugs or engine performance issues. The kind of the worst ones are regressions over time. as you operate a replica for an extended period, you might see performance
[54:55] degradation from issues that only arise once the thing has been running for an extended period. Um then you also might observe, especially in a heterogeneous cloud deployment environment, differences across replicas in their performance um that then need to be investigated. So the goal with these as with all kinds of like production debugging is observability. Um if you can log enough information uh to debug just from those logs, then you don’t have to spin up a development server and take a bunch of extra time. Um and so this runs from
[55:25] application level stuff um to I think focusing on the engine problems, model quality. You’ll you want to be able you want to be good at running evals for model quality as part of your deployment. Um so you want to hit your actual deployment with like evaluation benchmarking type scripts. Um there’s scripts in SUAN, there scripts in VLM, there’s external benchmarking tools. Um take those uh calculate some numbers, log traces somewhere, log as much data as you can about those so you can debug later. Um and then in prod log traces
[55:57] and try to get feedback from users on correctness that you can thread through all the way back to those requests. Um for tokenizer bugs, hot tip, log the token IDs. Set your engine up so that the token IDs are logged as well. Um and then finally for performance, performance bugs can show up anywhere. So the more metrics that you log uh the better like more metrics than you think you need um because you never know which crossorrelation will re will make the bug super easy to discover instead of super hard. Um so rather than going through those I’m going to talk a little bit about
[56:28] this. This is an endpoint dashboard on modal and what like how you might read it. Um I was operating that um the uh art project uh that I showed at the beginning and I just actually spun up like three different replicas of it uh to see what would happen like under increased load. Um so there was a traffic spike which you can see down here that turns into increasing uh first time to first token as things start to cue uh on the prefill side and and don’t enter prefill um until uh the things
[56:58] have finished. uh and then also shows up as well as inter token latency as uh decodes get first get a little bit slower and then a lot slower with this queuing as there’s sort of contention on the underlying GPU. Um and so the solution in this case with going back to the question about serverless deployments is this is detected as like increased load. You spin up new replicas. Those replicas come online. They start taking on some of this request load um and then the um like the amount cued um uh decreases
[57:31] back down to baseline and things are um you your congestion is solved. So I think um that’s the like primary way. I think in many cases, you know, we scale up additional resources to solve congestion and queuing. Um, so yeah. Um, when you need to go deeper, I’ll skip over this. Um, but there’s the NSI systems or torch profiler is one of your key tools here. It allows you to see the whole engine at once. Um, allows you to see all the processes that are running, all the work that’s going on on the GPU.
[58:02] Um, and pick out issues like, oh, this GPU is running uh like everything’s slower than the others. turned out to be a NUMA awareness problem. Um, okay. So, uh, closing out here, um, if you want to learn more, mini SGA and Nano VLM are a really great place to start. These are these simple implementations of the engine architecture, um, by the two teams. They’re great for humans. Um, they’re incredible for agents. They can fit all the context in, you know, in their context window. Uh, Alexa Gordic did a
[58:33] great walkthrough blog of VLM. uh and uh share these slides later. Um also work through that in a notebook so you can play with it and then actually uh you can point your coding agent at the coding base and ask questions. Cognition al also operates this thing called deep wiki which basically crawls these a bunch of repos including sing and vlm and writes nice architecture diagrams and makes this like wiki style um page where you can sort of read and ask and then ask their um agent questions which
[59:04] is a nice thing to pair with your own agent setup. Um so that’s everything. I’m out of time. So I’ll just quickly say if you’re interested in deploying and owning your own inference, we we have a a new product on modal to help you like get started with deployment really easily. Get optim get an initial baseline of reasonably optimized uh inference engine deployment that you can work off of with modal endpoints. Um so check that out and if you’re interested in working on deploying inference for
[59:35] hundreds of of teams, uh check out modal.j jobs. Thank you. >> [applause] [music]