Modelos pequenos estão se tornando rapidamente mais capazes e aplicáveis em uma ampla variedade de casos de uso empresariais. Ao mesmo tempo, cada nova geração de GPUs oferece drasticamente mais compute e largura de banda de memória. O resultado? Mesmo sob cargas de trabalho de alta simultaneidade, LLMs pequenos geralmente deixam uma grande parte do compute da GPU e da largura de banda da memória parados.
Com casos de uso como conclusão de código, recuperação, correção gramatical ou modelos especializados, nossos clientes corporativos usam muitos desses modelos de linguagem pequenos no Databricks, e estamos constantemente exigindo o máximo das GPUs. O Serviço de Múltiplos Processos (MPS) da NVIDIA parecia uma ferramenta promissora: ele permite que vários processos de inferência compartilhem um único contexto de GPU, permitindo que suas operações de memória e compute se sobreponham — na prática, extraindo muito mais trabalho do mesmo hardware.
Nós nos propusemos a testar rigorosamente se o MPS oferece maior throughput por GPU em nossos ambientes de produção. Descobrimos que o MPS oferece ganhos significativos de throughput nestes regimes:
- Modelos de linguagem muito pequenos (≤3B de parâmetros) com contexto de curto a médio (<2k tokens)
- Modelos de linguagem muito pequenos (<3B) em cargas de trabalho apenas de pré-preenchimento
- Motores com sobrecarga significativa de CPU
A explicação principal, com base em nossos estudos de ablação, é dupla: no nível da GPU, o MPS permite uma sobreposição significativa de kernels quando motores individuais deixam a compute ou a largura de banda da memória subutilizadas — especialmente durante fases dominadas por atenção em modelos pequenos; e, como um efeito colateral útil, ele também pode mitigar gargalos de CPU, como sobrecarga do programador ou sobrecarga de processamento de imagem em cargas de trabalho multimodais, ao fragmentar o lote total entre os motores, reduzindo a carga de CPU por motor.
O que é MPS?
O Serviço de Múltiplos Processos (MPS) da NVIDIA é um recurso que permite que vários processos compartilhem uma única GPU de forma mais eficiente, multiplexando seus kernels CUDA no hardware. Como diz a documentação oficial da NVIDIA:
O Multi-Process serviço (MPS) é uma implementação alternativa e compatível em nível binário da Interface de Programação de Aplicações (API) CUDA. A arquitetura de Runtime do MPS foi projetada para permitir de forma transparente aplicações CUDA cooperativas de múltiplos processos.
Em termos mais simples, o MPS fornece uma implementação CUDA compatível com binários dentro do driver que permite que vários processos (como mecanismos de inferência) compartilhem a GPU de forma mais eficiente. Em vez de os processos serializarem o acesso (e deixarem a GPU parada entre as execuções), seus kernels e operações de memória são multiplexados e sobrepostos pelo servidor MPS quando os recursos estão disponíveis.
O Cenário de Escalonamento: Quando o MPS Ajuda?
Em uma determinada configuração de hardware, a utilização efetiva depende muito do tamanho do modelo, da arquitetura e do comprimento do contexto. Como os modelos de linguagem grandes recentes tendem a convergir para arquiteturas semelhantes, usamos a família de modelos Qwen2.5 como um exemplo representativo para explorar o impacto do tamanho do modelo e do comprimento do contexto.
Os experimentos abaixo compararam dois motores de inferência idênticos executando na mesma GPU NVIDIA H100 (com o MPS ativado) com uma linha de base de instância única, usando cargas de trabalho homogêneas perfeitamente balanceadas.
Principais observações do estudo de escalonamento:
- O MPS oferece um aumento de >50% no throughput para modelos pequenos com contextos curtos
- Os ganhos caem de forma log-linear à medida que o comprimento do contexto aumenta — para o mesmo tamanho de modelo.
- Os ganhos também diminuem rapidamente à medida que o tamanho do modelo aumenta — mesmo em contextos curtos.
- Para o modelo 7B ou contexto 2k, o benefício cai abaixo de 10% e, eventualmente, incorre em uma lentidão.
Principais observações do estudo de escalonamento em carga de trabalho pesada de prefill
- Modelos Pequenos (<3B): o MPS oferece consistentemente uma melhora no throughput de mais de 100%.
- Modelos de tamanho médio (~3B): os benefícios diminuem à medida que o comprimento do contexto aumenta, levando eventualmente a uma regressão no desempenho.
- Modelos Grandes (>3B): o MPS não oferece nenhum benefício de desempenho para esses tamanhos de modelo.
Os resultados de escalonamento acima mostram que os benefícios do MPS são mais pronunciados para configurações de baixa utilização de GPU, modelo pequeno e contexto curto, o que facilita a sobreposição eficaz.
Analisando os ganhos: de onde realmente vêm os benefícios do MPS?
Para identificar exatamente o porquê, decompusemos o problema nos dois blocos de construção principais dos transformadores modernos: as camadas MLP (perceptron multicamadas) e o mecanismo de Attention. Ao isolar cada componente (e remover outros fatores de confusão como a sobrecarga da CPU), pudemos atribuir os ganhos com mais precisão.
Recursos de GPU Necessários | |||
| N = Comprimento do Contexto | Pré-preenchimento (compute) | Decodificação (Largura de Banda de Memória) | Decodificação (compute) |
| MLP | O(N) | O(1) | O(1) |
| Attn | O(N^2) | O(N) | O(N) |
Os Transformers consistem em camadas de Atenção e MLP com comportamento de escalonamento diferente:
- MLP: carrega os pesos uma vez; processa cada token de forma independente -> largura de banda de memória e compute constantes por token.
- Attention: Carrega o cache KV e calcula o produto com todos os tokens anteriores → Largura de banda de memória linear e compute por token.
Com isso em mente, realizamos estudos de ablação direcionados.
Modelos apenas com MLP (Attention removido)
Para modelos pequenos, a camada MLP pode não saturar o compute mesmo com mais tokens por lotes. Isolamos o impacto do MLP removendo o bloco de atenção do modelo.
Como mostrado na figura acima, os ganhos são modestos e desaparecem rapidamente. À medida que o tamanho do modelo ou o comprimento do contexto aumenta, um único engine já satura o compute (mais FLOPs por token em MLPs maiores, mais tokens com sequências mais longas). Uma vez que um engine está limitado por compute, executar dois engines saturados não oferece quase nenhum benefício — 1 + 1 <= 1.
Modelos apenas com Attention (MLP removido)
Depois de ver ganhos limitados com o MLP, pegamos o Qwen2.5-3B e medimos a configuração somente de atenção de forma análoga.
Os resultados foram surpreendentes:
- Cargas de trabalho somente de atenção mostram ganhos de MPS significativamente maiores do que o modelo completo, tanto para pré-preenchimento (prefill) quanto para decodificação.
- Para decode, os ganhos diminuem linearmente com o comprimento do contexto, o que está alinhado com nossa expectativa de que, na fase de decode, os requisitos de recursos para o attention cresçam com o comprimento do contexto.
- Para prefill, os ganhos caíram mais rapidamente do que para decodificação.
O ganho de MPS vem puramente dos ganhos de atenção, ou existe algum efeito de sobreposição do MLP de Atenção? Para estudar isso, calculamos o Ganho Esperado do Modelo Completo como sendo uma média ponderada de Somente Atenção e Somente MLP, com os pesos sendo sua contribuição para o tempo de execução total. Este Ganho Esperado do Modelo Completo são basicamente ganhos puramente das sobreposições Attn-Attn e MLP-MLP, embora não leva em conta a sobreposição Attn-MLP.
Para a carga de trabalho de decode, o Ganho Esperado do Modelo Completo é ligeiramente maior que o ganho real, o que indica um impacto limitado da sobreposição Attn-MLP. Além disso, para a carga de trabalho de prefill, o Ganho Real do Modelo Completo é muito menor que os ganhos esperados a partir da sequência 128. Uma explicação hipotética poderia ser que há menos oportunidades para o kernel de Attention não saturado ser sobreposto, porque o outro motor está gastando uma fração significativa de tempo realizando MLP saturado. Portanto, a maior parte do ganho do MPS vem de 2 motores com o attention não saturado.
Benefício bônus: recuperando o tempo de GPU perdido para a sobrecarga da CPU
Os estudos de ablação acima focaram em cargas de trabalho limitadas pela GPU, mas a forma mais severa de subutilização ocorre quando a GPU fica parada esperando por trabalho da CPU — como programador, tokenização ou pré-processamento de imagens em modelos multimodais.
Em uma configuração de engine único, essas paradas da CPU desperdiçam diretamente os ciclos da GPU. Com o MPS, um segundo engine pode assumir a GPU sempre que o primeiro estiver bloqueado na CPU, transformando o tempo ocioso em compute produtivo.
Para isolar esse efeito, escolhemos deliberadamente um regime onde os ganhos anteriores no nível da GPU haviam desaparecido: Gemma-4B (um tamanho e comprimento de contexto onde a atenção e o MLP já estão bem saturados, portanto, os benefícios de sobreposição de kernel são mínimos).
Com uma latência de 8s, o motor único de referência (azul) é limitado pela sobrecarga da CPU do programador, que pode ser eliminada habilitando a programação assíncrona no vLLM (linha verde, +33% de throughput) ou executando dois motores com MPS sem programação assíncrona (linha amarela, +35% de throughput). Esse ganho quase idêntico confirma que, em cenários com restrição de CPU, o MPS pode recuperar essencialmente o mesmo tempo parado da GPU que o agendamento assíncrono elimina. O MPS pode ser útil, já que o vLLM v1.0 vanilla ainda tem sobrecarga de CPU na camada do programador, onde otimizações como o agendamento assíncrono não estão totalmente disponíveis.
Uma Bala, Não uma Bala de Prata
Com base em nossos experimentos, o MPS pode proporcionar ganhos significativos para a inferência de modelos pequenos em algumas zonas operacionais:
- Motores com sobrecarga significativa de CPU
- Modelos de linguagem muito pequenos (≤3B de parâmetros) com contexto de curto a médio (<2k tokens)
- Modelos de linguagem muito pequenos (<3B) em cargas de trabalho pesadas de prefill
Fora desses pontos ideais (por exemplo, modelos 7B+, contexto longo >8k ou cargas de trabalho já limitadas pela compute), os benefícios no nível da GPU não podem ser capturados facilmente pelo MPS.
Por outro lado, o MPS também introduziu complexidade operacional:
- Peças móveis extras: daemon MPS, configuração de ambiente do cliente e um roteador/balanceador de carga para dividir o tráfego entre os engines
- Complexidade de depuração aumentada: sem isolamento entre os motores → um vazamento de memória ou OOM em um motor pode corromper ou encerrar todos os outros que compartilham a GPU
- Carga de monitoramento: agora precisamos observar a saúde do daemon, o estado da conexão do cliente, o balanceamento de carga entre motores, etc.
- Modos de falha frágeis: como todos os mecanismos compartilham um único contexto CUDA e daemon MPS, um único cliente com mau comportamento pode corromper ou esgotar os recursos de toda a GPU, afetando instantaneamente todos os mecanismos colocalizados.
Em suma: o MPS é uma ferramenta precisa e especializada — extremamente eficaz nos regimes restritos descritos acima, mas raramente uma vitória de propósito geral. Nós realmente gostamos de levar ao limite o compartilhamento de GPU e descobrir onde estão os verdadeiros abismos de desempenho. Ainda há uma enorme quantidade de desempenho e eficiência de custos inexplorados em toda a pilha de inferência. Se você se empolga com sistemas de serviço distribuídos ou em fazer LLMs rodarem 10x mais barato em produção, estamos contratando!
Autores: Xiaotong Jiang
(Esta publicação no blog foi traduzida utilizando ferramentas baseadas em inteligência artificial) Publicação original
