Artigo

Menor Latência e Maior Throughput com Implantação Multi-Node DeepSeek

Baleia brilhante, referenciando DeepSeek

Na maioria dos sistemas, latência e vazão costumam ser objetivos conflitantes que exigem compensações durante o design e a implantação. Por exemplo, em modelos de linguagem grandes e densos, aumentar o tamanho do lote pode melhorar a vazão, mas também aumenta a latência; aumentar o paralelismo de tensores dentro de uma única máquina pode reduzir a latência, mas diminui o número de réplicas, levando a uma menor vazão.

Modelos de Mistura de Especialistas (MoE) como o DeepSeek-V3/R1 demonstraram recentemente excelentes capacidades de modelagem e eficiência operacional. Por exemplo, o modelo DeepSeek-V3/R1 possui 671 bilhões de parâmetros no total, mas cada token utiliza apenas 37 bilhões de parâmetros durante a inferência. Essa arquitetura de modelo apresenta tanto desafios quanto oportunidades para sistemas de inferência.

Este artigo demonstra que, ao contrário dos sistemas convencionais, modelos MoE como o DeepSeek-V3/R1 podem simultaneamente alcançar maior vazão e menor latência ao utilizar mais GPUs em implantações de múltiplos nós na maioria dos cenários.


Arquiteturas de Implantação

Devido ao grande número de pequenos especialistas que o modelo possui, as implantações devem ser distribuídas por vários dispositivos. Consideramos tanto implantações de nó único em um único nó com 8xH200 GPUs, quanto implantações de múltiplos nós em 8xH100 GPUs.

Ambas as arquiteturas de implantação aproveitam o Paralelismo de Dados, orquestrado através do nosso programador de solicitações personalizado. A implementação do paralelismo de dados envolve o lançamento de várias instâncias do motor de inferência, cada uma operando de forma independente para servir e manter as solicitações. O programador de solicitações, que interage com o motor via GRPC, é responsável por distribuir as solicitações de forma o mais uniforme possível, ao mesmo tempo que facilita a reutilização de KV, enviando solicitações com prefixo parcialmente correspondido para os servidores contendo o cache. As instâncias do motor não se estendem por vários nós. Elas podem opcionalmente usar o paralelismo de tensores para distribuir a atenção por vários dispositivos. As instâncias são interconectadas via NVLink no caso de nó único ou InfiniBand para o caso de múltiplos nós, despachando e coletando especialistas.

A configuração de implantação de nó único oferece uma latência superior com pequenos tamanhos de lote; no entanto, o desempenho degrada rapidamente sob condições de carga aumentada.

Para implantar o motor de atendimento, iniciamos um pod por nó hospedando múltiplas instâncias do motor. O PyTorch é responsável por configurar a comunicação distribuída e negociar a inicialização do NVSHMEM. Para comunicação, confiamos em kernels CUDA personalizados descritos em um post no blog anterior. A implementação das duas implantações é virtualmente idêntica, com o modelo selecionando os kernels corretos para usar com base no fabric que implementa o paralelismo de especialistas.


Técnicas de Paralelização

Antes de mergulhar em nossas comparações de desempenho, é essencial entender as principais estratégias de paralelização que tornam a implantação de modelos MoE massivos como o DeepSeek-V3/R1 possível.

Paralelismo de Tensor

No caso de inferência de modelos de linguagem grande (LLM), o Paralelismo de Tensor (TP) é normalmente usado para reduzir o uso de memória e a computação por GPU, reduzindo assim a latência. Geralmente, podemos fragmentar Projeções Lineares nas Camadas de Atenção e MLP ao longo das dimensões de linha ou coluna, e fragmentar operações de Atenção ao longo da dimensão de cabeça de atenção.

Com TP, a arquitetura Llama-3 não tem computação duplicada para Projeções Lineares e operações de Atenção entre GPUs, o que é um método de fragmentação ideal. No entanto, em modelos DeepSeek-V3/R1, TP não pode atingir isso.

Modelos DeepSeek-V3/R1 usam Atenção Multi-Latente (MLA). Uma Camada MLA primeiro usa uma Projeção Linear kv_a_proj para calcular o vetor latente, depois utiliza outra Projeção Linear kv_b_proj para transformá-lo no espaço de cada cabeça de atenção. Como todas as cabeças de atenção compartilham o mesmo vetor latente, TP não pode fragmentar o vetor latente, então todos os Ranks TP precisam replicar os parâmetros e a computação de kv_a_proj e kv_b_proj. Da mesma forma, como o MLA armazena o vetor latente no KV Cache, cada Rank TP armazena uma cópia idêntica do KV Cache.

Apesar de alguma duplicação no MLA, o Paralelismo de Tensor ainda proporciona uma redução parcial nas demandas computacionais, tornando-o valioso para cenários que exigem altas velocidades de saída.

Paralelismo de Especialistas

Modelos DeepSeek-V3/R1 substituem Camadas MLP por Camadas MoE. Uma Camada MoE possui 256 especialistas roteados e um especialista compartilhado. Cada token é despachado para 8 diferentes especialistas roteados para computação, e os resultados são somados ponderadamente. Cada token também realiza cálculos no especialista compartilhado, e o resultado é adicionado ao resultado dos especialistas roteados.

O Paralelismo de Especialistas (EP) serve como a abordagem típica de fragmentação para Camadas MoE, com cada GPU gerenciando 256 / EP especialistas roteados enquanto mantém uma cópia do especialista compartilhado. Comparado ao TP, a vantagem do EP é que ele pode distribuir a computação por mais GPUs, reduzindo o uso de computação e memória por GPU.

Antes de realizar a computação de especialistas, todas as GPUs precisam realizar uma comunicação AllToAll para despachar tokens para as GPUs onde os especialistas correspondentes estão localizados; após a computação dos especialistas, outra comunicação AllToAll é necessária para coletar os resultados da computação de várias GPUs e realizar a soma ponderada. Implementamos uma versão otimizada desses dois Kernels de comunicação AllToAll, Dispatch e Combine, usando NVSHMEM. Em nosso post no blog anterior, detalhamos a implementação, e nossos kernels foram open-sourced no GitHub.

Paralelismo de Dados

Com o EP, podemos distribuir a computação MoE por 128 ou ainda mais GPUs. No entanto, a computação MLA não pode ser particionada com EP. Neste ponto, podemos introduzir o Paralelismo de Dados (DP). Cada Grupo DP possui uma cópia completa da Camada MLA. Cada Grupo DP aceita diferentes entradas e realiza computação da Camada MLA independentemente.

O Paralelismo de dados da Camada MLA e o Paralelismo de Tensor podem ser combinados, com um Grupo DP sendo dividido em múltiplos Ranks TP. O Paralelismo de Especialistas da Camada MoE pode ser combinado com o Paralelismo de Dados/Paralelismo de Tensor da Camada MLA. EP = DP * TP. Por exemplo, em 16 máquinas, EP128 DP32 TP4 significa distribuir especialistas roteados por 128 GPUs, com cada 4 GPUs formando um Grupo DP, para um total de 32 Grupos DP independentes.


Nó Único vs Múltiplos Nós

Os 671 bilhões de parâmetros do DeepSeek excedem a capacidade de memória de uma única máquina H100 de 8 GPUs (80 GB * 8), mas uma única máquina H200 de 8 GPUs pode acomodar totalmente o modelo inteiro (141 GB * 8). Usando a configuração EP8 DP8 TP1, o modelo utiliza cerca de 100 GB de memória por GPU, deixando aproximadamente 40 GB para KV Cache e outros resultados intermediários. Um token ocupa 70.272 bytes de KV Cache. Assumindo que cada solicitação tenha 5.000 tokens, cada GPU pode acomodar cerca de 100 solicitações.

Queríamos entender as diferenças de desempenho entre implantações de nó único e múltiplos nós sob diferentes configurações. Usamos uma máquina H200 para implantação de nó único e até 16 máquinas H100 para implantações de múltiplos nós. Para cada ambiente de implantação, usamos combinações de TP 1, 2, 4, 8, e tamanhos de lote por GPU de 1, 2, 4, 8, 16, 32, 64, 128. Assumimos que cada solicitação tinha um comprimento de KV Cache de 5.000 tokens. Também assumimos que a Previsão de Múltiplos Tokens (MTP) prevê 1 token adicional (ou seja, o comprimento da consulta de cada solicitação é 2) e assumimos, de forma conservadora, uma taxa de aceitação de 60%. A figura abaixo mostra a vazão e a velocidade de saída para diferentes configurações.

O eixo horizontal representa a velocidade de saída por solicitação em tokens/s. O eixo vertical usa uma escala logarítmica para mostrar a vazão por máquina em tokens/s. Marcamos a Fronteira de Pareto para cada configuração EP com linhas de cores diferentes.

Em cenários com requisitos extremamente altos de velocidade de saída, usar um único nó EP8 DP1 TP8 com um tamanho de lote de 1 pode alcançar uma velocidade de saída superior a 100 tokens/s, mas a vazão é extremamente baixa, equivalente à velocidade de saída. Nesse cenário, o lote inteiro tem apenas 2 tokens, que podem ser despachados para no máximo 2*8=16 especialistas, ativando um total de no máximo 57 bilhões de parâmetros.

No intervalo de velocidade de saída de 80-40 tokens/s, à medida que a vazão aumenta, a velocidade de saída diminui significativamente. Em contraste, EP128 tem cerca de 5 vezes maior vazão que a implantação de nó único na mesma velocidade de saída.

Esse fenômeno pode ser explicado examinando como as implantações de nó único se comportam: aumentar o tamanho do lote está diretamente correlacionado com um aumento nos especialistas ativados. Quando o tamanho do lote é 1, o número médio de especialistas ativados por GPU é 2 * 8 / 8 = 2. Quando o lote é grande o suficiente, todos os especialistas são ativados, significando que cada GPU ativa 256 / 8 = 32 especialistas. Ativar mais especialistas significa que a GPU precisa ler mais parâmetros da memória, aumentando significativamente a pressão da largura de banda da memória. Como a fase de decodificação de grandes modelos de linguagem já é um gargalo devido à largura de banda da memória e não ao desempenho de computação, aumentar o tamanho do lote na implantação de nó único reduz significativamente a velocidade de saída.

A comparação das quatro configurações de implantação de múltiplos nós (EP16, EP32, EP64 e EP128) revela que valores EP mais altos deslocam a Fronteira de Pareto em direção a melhorias simultâneas na vazão e na velocidade de saída.

Usar um número EP mais alto significa que cada GPU é alocada com menos especialistas. Por exemplo, EP128 significa que cada GPU é responsável por 256 / 128 = 2 especialistas, então a pressão da largura de banda da memória é significativamente reduzida. Em outras palavras, usando um número EP maior, ganhamos efetivamente mais largura de banda de memória. Quando o tamanho do lote por GPU é menor que 64, aumentar o tamanho do lote não afeta significativamente a velocidade de computação dos especialistas porque aumentar o número de entradas não aumenta significativamente a pressão da largura de banda de memória. Portanto, observamos que ao usar EP128, aumentar o tamanho do lote não afeta a velocidade de saída tão significativamente.

Curiosamente, em tamanhos de lote maiores (64 solicitações por GPU), observamos um novo fenômeno: a vazão de implantação de nó único é ligeiramente maior que a de múltiplos nós. Parte da razão é que o NVLink intra-no tem maior largura de banda que o InfiniBand inter-no. Outra parte se deve às limitações em nossa implementação. Analisaremos esse fenômeno em mais detalhes posteriormente.

Devido às limitações de capacidade de memória, a configuração EP8 DP8 TP1 não pode atingir um tamanho de lote de 128 por GPU, então a implantação de múltiplos nós ainda é uma melhor escolha em cenários que buscam maior vazão.


Sobreposição de Computação e Comunicação

Conforme introduzido brevemente acima em relação ao Paralelismo de Especialistas, as GPUs ficam ociosas durante a comunicação da Camada MoE. Para reduzir o desperdício e diminuir a latência, precisamos encontrar tarefas de computação independentes de dados para preencher esse tempo ocioso.

A parte superior da figura acima mostra o fluxo de computação de uma camada. A computação MoE depende de Dispatch, e a computação da próxima camada depende do resultado de Combine.

Colocamos o especialista compartilhado em cada GPU. Dessa forma, a computação do especialista compartilhado não requer comunicação AllToAll. Portanto, podemos realizar a computação do especialista compartilhado imediatamente após o envio de Dispatch, depois esperar que o Dispatch Recv seja concluído. Chamamos esse esquema de sobreposição de "Dispatch Overlap".

O Dispatch Overlap oferece implementação direta e ampla aplicabilidade. Essa técnica esconde o tempo de computação do especialista compartilhado em todos os tamanhos de EP e tamanhos de lote.

Para aumentar ainda mais a sobreposição de computação e comunicação, usamos microbatching mencionado no relatório técnico DeepSeek para quebrar a dependência de dados. Conforme mostrado na parte inferior da figura, dividimos a computação de uma Camada Transformadora em 5 estágios:

  • Estágio 1: NormInput, QKVProj, AppendKV, BMM

  • Estágio 2: BMM, Attn, OProj, Pós-Norm, Gate

  • Estágio 3: Envio de Dispatch, Especialista Compartilhado

  • Estágio 4: Recepção de Dispatch, MoE, Envio de Combine

  • Estágio 5: Recepção de Combine

Nas primeiras 3 Camadas Transformadoras Densas, usamos o lote inteiro. Nas 58 Camadas Transformadoras MoE seguintes, dividimos o lote uniformemente em dois microlotes. Os dois microlotes são executados alternadamente, com um deslocamento de 3 estágios. Como não há dependência de dados entre esses dois microlotes, podemos alternar para a computação de outro microlote após o envio do Dispatch e após o envio do Combine.


Análise de Latência

Em seguida, comparamos os efeitos da sobreposição através de um experimento, bem como comparamos as diferenças de desempenho entre a implantação de nó único EP8 e a implantação de múltiplos nós EP128. Para facilitar a comparação, usamos GPUs H100 para o experimento a seguir. Usamos TP1, um tamanho de lote de 128 por GPU, um comprimento de consulta de 2 por solicitação, e um comprimento de KV Cache de 5000.

A figura acima mostra o tempo total gasto em uma Camada Transformadora MoE e a proporção de latência de diferentes tipos de kernels. Exceto para Dispatch, Combine e GroupGEMM, o tempo de execução de outros kernels deve ser igual nas séries EP8, EP128 NoOverlap e EP128 DispatchOverlap porque o tamanho do lote é o mesmo.

Sobreposição

Vamos primeiro comparar os efeitos dos três métodos de sobreposição. NoOverlap levou 2667µs no total, DispatchOverlap levou 2651µs, economizando 16µs ou apenas 0,6%. MicroBatch mostrou uma melhoria muito significativa, levando 1896µs, uma aceleração de 29%. Tanto o tempo de Dispatch quanto o de Combine foram reduzidos significativamente. Dispatch diminuiu de 593µs para 367µs, e Combine de 1012µs para 237µs.

Observe que para kernels de computação, dividir um lote de tamanho 128 em dois lotes de tamanho 64 aumenta o tempo total de execução. Portanto, embora o tempo gasto na comunicação tenha sido reduzido em 1001µs, o tempo total foi reduzido apenas em 771µs. Explicaremos o motivo usando o modelo Roofline na seção a seguir.

Por esse motivo, o microbatching nem sempre melhora o desempenho.

A figura acima mostra a melhoria de desempenho do Microbatch em comparação com DispatchOverlap para tamanhos de lote de 4-128. Quando o tamanho do lote é menor que 32, o Microbatch reduz o desempenho em 5%-40%. Quando o tamanho do lote é maior ou igual a 32, o Microbatch pode melhorar o desempenho em 10%-35%.

EP8 vs EP128

Vamos voltar à figura anterior e comparar EP8 e EP128 Microbatch. EP8 levou 1802µs no total, ligeiramente menos que 1896µs do EP128. Além do aumento no tempo de execução do kernel trazido pelo Microbatch mencionado acima, as principais diferenças estão em GroupGEMM usado para computação MoE, e nos dois kernels de comunicação, Dispatch e Combine.

O GroupGEMM do EP8 levou 555µs, enquanto o do EP128 levou 270µs, reduzindo pela metade. Esta é a principal vantagem da implantação de múltiplos nós.

Infelizmente, o tempo gasto na comunicação aumentou em 213µs, o que compensou muito a vantagem do GroupGEMM. Em testes de desempenho separados de nossos kernels de comunicação, encontramos que eles só podem atingir metade da largura de banda do Infiniband. Continuaremos a otimizar nossos kernels de comunicação.

Outro kernel que apresenta desempenho abaixo do esperado é o GEMM. O Microbatch aumentou o tempo de GEMM em 95µs. Analisaremos o GEMM em mais profundidade na seção do modelo Roofline abaixo. Acreditamos que a implementação atual do GEMM ainda não atingiu o desempenho ideal.

Roofline

O Modelo Roofline é uma boa ferramenta para analisar o desempenho do kernel. Seu eixo horizontal é a Intensidade Aritmética, a relação de FLOP para bytes de E/S de memória. O valor do eixo horizontal pode ser calculado diretamente a partir da semântica do kernel. O eixo vertical representa o desempenho alcançado, calculado dividindo FLOP pelo tempo de latência do benchmark.

O limite teórico superior do desempenho do kernel é determinado diretamente pelas especificações da GPU. O pico de desempenho FP8 do H100 é de 1979 TFLOP/s, representado como uma linha horizontal no modelo Roofline. A largura de banda de memória do H100 é de 3,35 TB/s, representada como a inclinação de uma linha passando pela origem. As duas linhas determinam os limites de desempenho para kernels limitados por computação e memória, respectivamente.

A seguir, discutiremos o desempenho dos kernels GroupGEMM e GEMM.

GroupGEMM

O kernel GroupGEMM em MoE realiza a seguinte computação: Existem g grupos no total, o i-ésimo grupo possui m_i tokens, realizando uma multiplicação matricial de [m_i, k] x [k, n] -> [m_i, n]. Nos testes de desempenho, assumimos que o número de tokens em cada grupo é o mesmo, denotado como m_i = m. Então a contagem de FLOP para GroupGEMM é 2 * g * m * k * n, e os bytes de E/S de memória são g * (m * k + n * k + m * n).

No modelo DeepSeek-V3/R1, existem 256 especialistas, e cada token é despachado para 8 especialistas para computação. Assumindo um tamanho de lote de 128, comprimento de consulta de 2, usando a configuração EP128 DP128, o número médio de tokens recebidos por cada especialista (ou seja, m) é 128 * 2 * 8 * 128 / 256 = 1024. Da mesma forma, podemos calcular m para outras configurações e tamanhos de lote.

Usamos a implementação do GroupGEMM do DeepGEMM para testes de desempenho. Os pontos de teste cobriram combinações das configurações EP8, EP16, EP32, EP64, EP128 com TP1 e tamanhos de lote de 1-128.

A figura acima mostra o modelo Roofline para GroupGEMM sob diferentes configurações EP. Diferentes EP correspondem a diferentes números de grupos. A figura ilustra linhas de desempenho quase sobrepostas, indicando que o desempenho do GroupGEMM é predominantemente determinado pela contagem total de tokens (representada como g * m).

As estrelas marcam os pontos de dados correspondentes a um tamanho de lote de 128 por GPU para cada configuração EP. Comparando esses pontos de dados marcados com estrelas, podemos ver que à medida que o EP aumenta (e o DP aumenta de forma síncrona), o número de tokens por especialista m também aumenta. Em EP8, m=128, enquanto em EP128, m=2048.

Ao aumentar m, a Intensidade Aritmética também aumenta. Na maioria das configurações, o GroupGEMM é limitado pela largura de banda de memória, então aumentar m melhora o desempenho.

GEMM

O kernel GEMM corresponde a Projeções Lineares no modelo, como Projeção Q/K/V/O. Para uma multiplicação matricial de [m, k] x [k, n] -> [m, n], a contagem de FLOP é 2 * m * k * n, e os bytes de E/S de memória são m * k + n * k + m * n. Também podemos testar a latência para tamanhos de lote de 1-128.

A figura acima mostra o modelo Roofline para GEMM sob diferentes configurações EP. Podemos ver que o desempenho do GEMM é limitado pela largura de banda de memória. À medida que o tamanho do lote aumenta, a Intensidade Aritmética também aumenta, melhorando assim o desempenho.

Microbatch

Ao usar microbatching, dividimos o lote igualmente em duas partes. A partir das duas figuras acima, podemos ver que quando m se torna m/2, a eficiência da multiplicação de matrizes diminui. Portanto, executar duas multiplicações de matrizes de tamanho m/2 leva mais tempo do que executar uma multiplicação de matrizes de tamanho m.

Previsão de Múltiplos Tokens

Ao longo deste artigo, presumimos o uso de Previsão de Múltiplos Tokens (MTP) para decodificação especulativa. O MTP altera o comprimento da consulta por solicitação de 1 para 2. Para multiplicação de matrizes, isso é equivalente a alterar m para m * 2, aumentando assim a eficiência da multiplicação de matrizes. Por outro lado, se desenharmos o modelo Roofline para MLA, descobriríamos que aumentar o comprimento da consulta aumenta significativamente a eficiência do kernel MLA.

Portanto, o uso de MTP desempenha um papel importante na eficiência do modelo.

Implementação e Otimizações

Nesta seção, vamos apresentar alguns detalhes de implementação e otimização para nosso modelo DeepSeek-V3/R1.

Quantização

O DeepSeek-V3/R1 foi treinado nativamente em FP8 usando um esquema de quantização por bloco, com pesos quantizados estaticamente e ativações quantizadas dinamicamente. Em vez de calcular um fator de escala por canal ou por matriz estaticamente, os fatores de escala são calculados em vetores de 128 elementos para ativações e em blocos de 128x128 elementos para matrizes, limitando a degradação de precisão devido à quantização.

Na Perplexity, confiamos em uma mistura de kernels CUDA e Triton para suportar a inferência, com o CUDA sendo usado para os kernels mais sensíveis ao desempenho e infrequentemente modificados (como atenção e GEMM), com o Triton implementando uma ampla gama de kernels de ativação, normalização e utilitários. O Triton nos permitiu adaptar rapidamente os kernels ao esquema de quantização por bloco.

Para camadas lineares e MoE, misturamos os kernels Deep GEMM com nossos próprios kernels Triton GEMM, pois notamos que para certas dimensões de matriz e baixos tamanhos de lote, o Split-K oferece menor latência. Se a camada não quantizada realiza uma multiplicação de (M, K) x (K, N), precisa de (M x ceil_div(K, 128)) x (ceil_div(K, 128), ceil_div(N, 128)) fator de escala para quantização em bloco. Para quantização em bloco, os fatores de escala para ativações são calculados dinamicamente, em vez de serem pré-calibrados. Como os fatores de escala de ativação são agregados apenas ao longo da dimensão K e não ao longo da dimensão M, os kernels requerem apenas pequenas alterações para suportar o esquema.

A função de ativação SiLU usada pelo DeepSeek-V3/R1 exigiu mudanças substanciais para suportar gráficos CUDA, quantização em bloco e contagens de tokens roteados dinamicamente. A quantização em bloco pode ser problemática, pois introduz reduções horizontais; no entanto, o kernel já dividia ativações ao longo de sua dimensão ocultada em blocos de 1024 elementos. Dentro de um bloco, o tensor a ser quantizado era ainda dividido em blocos de 128 para computar o maior valor absoluto, com o Triton gerando reduções eficazes de máximo entre grupos, adicionando overhead mínimo.

Para suportar roteamento MoE sob gráficos CUDA, os kernels devem estar cientes das informações de roteamento que indicam o número de tokens por especialista, em vez de agendar trabalho com base no tamanho dos buffers que foram alocados para conter o limite superior das contagens de tokens. Não podemos dividir o problema com base nas dimensões do tensor de entrada, então lançamos um número fixo de kernels persistentes que leem as informações de roteamento para determinar quantos tokens estão populados e dividem o trabalho de processar as ativações entre eles dinamicamente.

Já transferimos alguns de nossos kernels para o projeto FlashInfer e, no futuro, estaremos open-sourcing mais do nosso código.

Camada MLA

Utilizamos o FlashInfer para computação MLA. O FlashInfer suporta configurações flexíveis de Tabela de Página e desempenho extremamente alto.

Fundimos q_a_proj e kv_a_proj em um único qkv_a_proj. A latência diminuiu de 15,4 µs + 14,8 µs = 30,2 µs para 16,7 µs.

Decompomos kv_b_proj em duas matrizes, k_b_proj e v_b_proj. Escrevemos um kernel BMM Quantizado em Bloco FP8 para cálculos relacionados a essas duas matrizes.

Gráfico Cuda

O Gráfico Cuda pode reduzir significativamente o overhead de lançamento de kernels, o que é crucial para o desempenho. Criamos um Gráfico Cuda para cada tamanho de lote.

Antes de desenvolver nosso Kernel AllToAll, usávamos torch.all_to_all_single() para comunicação AllToAll. Essa operação requer que todas as GPUs usem o mesmo tamanho de lote. No entanto, diferentes Grupos DP podem executar tamanhos de lote diferentes.

Para garantir que all_to_all_single() seja compatível com diferentes Grupos DP usando tamanhos de lote diferentes, primeiro usamos uma operação allreduce() antes de cada execução do modelo para obter o tamanho máximo do lote entre todos os Grupos DP. Em seguida, fizemos com que todos os Grupos DP usassem esse tamanho de lote para executar.

Embora essa abordagem garanta que possamos usar Gráficos Cuda, ela possui três desvantagens. Primeiro, requer uma operação allreduce() adicional. Segundo, Grupos DP com tamanhos de lote menores são forçados a preencher. Terceiro, torna nosso código de implementação complexo.

Após implementar nosso próprio Kernel AllToAll, não precisamos mais que todas as GPUs usem o mesmo tamanho de lote. Portanto, não precisamos mais realizar operações allreduce() adicionais ou preencher tamanhos de lote.

Roteador MoE

O roteador MoE é implementado em Triton, contando com uma classificação modificada derivada da biblioteca padrão que também rastreia os índices dos elementos classificados. A implementação é compartilhada entre todos os modelos MoE, já que o roteamento Mixtral é um caso especial das rotas do DeepSeek, onde o grupo Top-K é o mesmo que o grupo de todos os especialistas. Os kernels esparsos consomem diretamente os índices e escores do Top-K, enquanto os esquemas de despacho/combinação densos que dependem de all-to-all exigem que as informações de roteamento sejam agregadas por especialista em vez de uma base por token.

Trabalho Futuro

No trabalho futuro, planejamos otimizar ainda mais o desempenho do modelo DeepSeek.

A próxima otimização mais importante é a Desagregação Prefill. As fases Prefill e Decode do modelo DeepSeek-V3/R1 têm características computacionais muito diferentes. Ambas podem usar diferentes estratégias de otimização e esquemas de implantação.

Para a Camada MLA, na fase Decode, usamos Absorção de Matriz para reduzir a contagem de FLOP da computação MLA. Na fase Prefill, primeiro projetar o vetor latente no espaço K/V e depois computar na forma de Atenção de Cabeça Múltipla (MHA) teria um desempenho melhor.

Se Prefill e Decode rodarem na mesma GPU, para reduzir o impacto do Prefill na velocidade de saída do Decode, normalmente usamos preenchimento fragmentado para dividir a consulta em vários fragmentos para o Prefill. Como o KV Cache armazena o vetor latente, se torna difícil converter MLA em forma MHA.

Para a Camada MoE, na fase Decode, usamos EP e DP tão grandes quanto possível para aumentar o número de tokens de entrada por especialista, melhorando assim o desempenho do GroupGEMM. Na fase Prefill, como o número de tokens já é grande o suficiente, o GroupGEMM já está limitado por computação. Portanto, para Prefill, podemos usar EP e DP menores.

Se Prefill e Decode rodarem na mesma GPU, desde que qualquer Grupo DP esteja executando Prefill, a latência das Camadas MoE em todas as GPUs aumentará, afetando significativamente a velocidade de saída do Decode.

Além da Desagregação Prefill, também planejamos otimizar os seguintes aspectos:

  • Desempenho AllToAll: Nosso kernel AllToAll atualmente só atinge 1/3 da largura de banda do Infiniband. Continuaremos a otimizar este kernel.

  • Decodificação especulativa estilo EAGLE: Nos dados acima, presumimos usar decodificação especulativa para prever 1 token. O EAGLE pode usar uma estrutura de árvore para prever múltiplos tokens, melhorando o comprimento de aceitação, o que pode aumentar significativamente a velocidade de saída.

  • Kernel GEMM: No Modelo Roofline mostrado anteriormente, podemos notar que a eficiência do kernel GEMM ainda está longe do limite teórico. Continuaremos a otimizar este kernel.

  • GB200 NVL72: Na mais recente solução GB200 NVL72 da NVIDIA, 72 GPUs Blackwell são interligadas via NVLink de alta velocidade. Para modelos de arquitetura MoE, isso representa uma grande oportunidade e desafio.

Conclusão

A implantação de múltiplos nós dos modelos MoE do DeepSeek alcança o que é tipicamente impossível com LLMs densos: melhorar simultaneamente tanto a vazão quanto a latência. Distribuindo especialistas por mais GPUs, reduzimos a pressão da largura de banda de memória por dispositivo, permitindo processamento mais rápido e maior vazão do sistema. Nossos experimentos mostram configurações EP128 atingindo até 5 vezes maior vazão em velocidades de saída equivalentes em comparação com implantações de nó único.

Técnicas de sobreposição de computação-comunicação como o microbatimento reduzem significativamente o overhead de comunicação de múltiplos nós, com nossa implementação mostrando até 40% de aumento de velocidade. Nossos kernels de comunicação AllToAll personalizados e implementações otimizadas de kernels possibilitaram a implantação eficiente do modelo de 671 bilhões de parâmetros.

À medida que as arquiteturas MoE ganham popularidade por sua capacidade, essas estratégias de implantação fornecem insights valiosos para escalar esses modelos de forma eficiente.

Referências

Interessado em moldar o futuro da nossa plataforma de API? Estamos contratando.

Junte-se à nossa comunidade de desenvolvedores para se manter atualizado sobre novos lançamentos, recursos e atualizações.

Interessado em moldar o futuro da nossa plataforma de API? Estamos contratando.

Junte-se à nossa comunidade de desenvolvedores para se manter atualizado sobre novos lançamentos, recursos e atualizações.

Interessado em moldar o futuro da nossa plataforma de API? Estamos contratando.

Junte-se à nossa comunidade de desenvolvedores para se manter atualizado sobre novos lançamentos, recursos e atualizações.