View a markdown version of this page

Pré-preenchimento e decodificação desagregados para inferência HyperPod - SageMaker IA da Amazon

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

Pré-preenchimento e decodificação desagregados para inferência HyperPod

O Disaggregated Prefill and Decode (DPD) separa as duas fases da inferência do LLM, pré-preenchimento e decodificação, em pools de GPU dedicados e transfere o cache de chave-valor (KV) entre elas pelo Elastic Fabric Adapter (EFA) usando o Remote Direct Memory Access (RDMA). GPU-Direct

Quando o pré-preenchimento e a decodificação são executados na mesma GPU (localizada), uma única solicitação de contexto longo pode paralisar fluxos de tokens em andamento para outros clientes, aumentando a latência por token sob carga. O DPD remove essa interferência executando o pré-preenchimento vinculado à computação em um conjunto de GPUs e a decodificação limitada à largura de banda de memória em outro, produzindo uma latência mais previsível sob tráfego misto e permitindo que você escale cada fase de forma independente.

O operador de inferência gerencia a orquestração, que inclui o provisionamento do roteador, a conexão dos pods de pré-preenchimento e decodificação via LMCache e NIXL e a integração com a observabilidade. HyperPod Você pode habilitar o DPD adicionando uma pdSpec seção ao mesmo InferenceEndpointConfig recurso que você já usa para endpoints de inferência.

Quando o DPD ajuda

O DPD oferece o maior benefício quando todas as seguintes condições estão presentes:

  • Modelos grandes e densos — 70B+ parâmetros (por exemplo, Llama 3.3 70B).

  • Entradas longas — mais de 4.000 tokens de entrada. Inter-token a melhoria da latência (ITL) aumenta com o comprimento da entrada, pois preenchidos mais longos causam mais interferência de decodificação quando colocados.

  • Simultaneidade sustentada — mais de 2 solicitações por segundo. Sem solicitações simultâneas competindo pela mesma GPU, não há nada para desagregar.

  • Saídas moderadas ou longas — mais de 256 tokens de saída. Mais tokens de saída significam mais benefícios cumulativos da latência estável por token.

Se sua carga de trabalho tem entradas curtas, baixa simultaneidade ou usa modelos pequenos, uma implantação colocalizada padrão é mais simples e tem um bom desempenho.

Pré-requisitos

Antes de implantar endpoints de inferência que usam pré-preenchimento e decodificação desagregados, você precisa configurar os seguintes componentes em seu ambiente de desenvolvimento local:

  • AWS Interface de linha de comando (AWS CLI)

  • Acesso ao seu cluster HyperPod Amazon EKS via kubectl

  • Token Hugging Face que permite acesso de leitura ao respectivo ponto de verificação do modelo. Isso não é necessário se o ponto de verificação do modelo já estiver localizado em um bucket do Amazon S3.

  • Uma imagem de trabalho que inclui vLLM, LMCache, NVIDIA NIXL e o provedor EFA libfabric. As seguintes opções de imagem são suportadas:

    • DLC: public.ecr.aws/deep-learning-containers/vllm:server-hyperpod-cuda-v1.1

    • LMCache: lmcache/vllm-openai:v0.4.3

    Ambas as imagens incluem LMCache 0.4.3, vLLM 0.19.0 e NIXL 1.0.0.

  • HyperPod Operador de inferência versão 3.2 ou posterior instalado. O DPD não é suportado em versões anteriores. O operador é instalado por padrão nos clusters recém-criados do HyperPod Amazon EKS. Se você pretende usar um cluster existente, siga as instruções de instalação emConfigurando seus HyperPod clusters para implantação de modelos. Verifique sua versão:

    kubectl get deployment hyperpod-inference-operator-controller-manager \ -n hyperpod-inference-system \ -o jsonpath='{.spec.template.spec.containers[?(@.name=="manager")].image}{"\n"}'
Importante

O pré-preenchimento e a decodificação desagregados exigem EFA-capable instâncias com suporte a RDMA. GPU-Direct Os seguintes tipos de instância são compatíveis:ml.p5.48xlarge,ml.p5e.48xlarge,ml.p5en.48xlarge,ml.p6-b200.48xlarge,ml.p6-b300.48xlarge. Outros tipos de instância não são compatíveis com o DPD.

Implemente um endpoint de DPD

A maioria dos InferenceEndpointConfig campos é compartilhada com endpoints não DPD e documentada em. Implantar modelos de base e modelos personalizados e ajustados Para habilitar o DPD, adicione as seções a seguir ao seu manifesto.

Prefill-Decode Especificação: PDSpec

Declara a prefill/decode topologia e especifica os argumentos. A presença desse campo é o que torna o endpoint desagregado: o operador cria implantações separadas para pré-preenchimento e decodificação e as conecta por meio do roteador e do back-end LMCache PD.

pdSpec: prefillSpec: replicas: 1 resources: limits: nvidia.com/gpu: ${GPUS_PER_NODE} requests: nvidia.com/gpu: ${GPUS_PER_NODE} args: - "--gpu-memory-utilization" - "0.75" decodingSpec: replicas: 1 resources: limits: nvidia.com/gpu: ${GPUS_PER_NODE} requests: nvidia.com/gpu: ${GPUS_PER_NODE} routingThreshold: 4096
replicas

Dimensione, pré-preencha e decodifique de forma independente.

resources

Aplicado à especificação do pod da função. Top-levelworker.resourcesé ignorado para pods de DPD; os valores por função são substituídos.

routingThreshold

Limite de comprimento do token que encaminha as solicitações para o caminho desagregado. As solicitações que não atendem a esse limite ignoram o pré-preenchedor e vão diretamente para o decodificador.

args

Sinalizadores vLLM específicos para essa função. Mesclado worker.args na inicialização: os sinalizadores já inseridos worker.args são substituídos pelo valor por função; os sinalizadores que não estão presentes são anexados.

Variáveis de ambiente do DPD: Variáveis de ambiente

Essas variáveis de ambiente são aplicadas de forma idêntica aos contêineres do pré-preenchedor e do decodificador; não há campo env-var por função. Para comportamento por função, use pdSpec.{prefillSpec,decodingSpec}.args em vez disso.

environmentVariables: - name: PD_BUFFER_SIZE value: "8589934592" - name: LMCACHE_SAVE_DECODE_CACHE value: "False" - name: PYTHONHASHSEED value: "0"
PD_BUFFER_SIZE(8 GiB)

Buffer de GPU reservado no decodificador para transferências de cache de entrada em KV, dimensionado por classificação. Para o Llama 70B com TP=8, o cache KV de cada token é de aproximadamente 40 KB por classificação, portanto, um prompt de 6.000 tokens ocupa aproximadamente 0,23 GB por classificação e 8 GiB contém aproximadamente 35 dessas transferências em voo. Quando o buffer excede a capacidade, os registros do decodificador Failed to allocate memory object, retrying... e os clientes veem picos de latência. Aumente para 16/32 GiB ou escale, decodingSpec.replicas se necessário.

LMCACHE_SAVE_DECODE_CACHE: "False"

Desativa o cache L1 redundante no decodificador. O pré-preenchedor é a fonte confiável dos acessos ao cache.

PYTHONHASHSEED: "0"

O LMCache usa o Python integrado hash() para calcular as chaves de cache do token de prompt. O Python randomiza essa semente de hash por processo por padrão, então prompts idênticos produzem chaves diferentes no pré-preenchedor e no decodificador, e as pesquisas falham. Fixar a semente faz com que as chaves concordem entre os frutos.

Configurar a estratégia de roteamento

A intelligentRoutingSpec seção define a estratégia de roteamento que o roteador DPD usa para selecionar um pré-preenchedor para cada solicitação. O roteador é criado automaticamente quando pdSpec está presente; esta seção é opcional e o padrão é. prefixaware

intelligentRoutingSpec: enabled: true routingStrategy: prefixaware

O DPD também pode ser integrado com roteamento inteligente e cache KV. Para obter mais informações, consulte Configure o cache KV e o roteamento inteligente.

Com uma única réplica pré-preenchida, todas as estratégias são direcionadas para essa réplica. A escolha só afeta o comportamento quandoprefillSpec.replicas > 1:

  • Para uma única réplica pré-preenchida, use prefixaware (o padrão) para maximizar os acessos ao cache KV quando as solicitações compartilham prefixos comuns, como solicitações do sistema ou histórico de bate-papo.

  • Para várias réplicas de pré-preenchimento, use roundrobin para distribuir a carga uniformemente entre as réplicas e evitar a interferência de um único pré-preenchedor.

Exemplo completo

O manifesto a seguir implanta o Llama 3.3 70B em duas instâncias ml.p5.48xlarge (um pré-preenchedor, um decodificador):

apiVersion: inference.sagemaker.aws.amazon.com/v1 kind: InferenceEndpointConfig metadata: name: dpd-test namespace: default spec: endpointName: dpd-test instanceType: ml.p5.48xlarge invocationEndpoint: v1/chat/completions modelName: Llama-3.3-70B-Instruct modelSourceConfig: modelSourceType: s3 modelLocation: Llama-3.3-70B-Instruct s3Storage: bucketName: <YOUR_BUCKET> region: <YOUR_REGION> loadBalancer: healthCheckPath: /health metrics: enabled: true kvCacheSpec: enableL1Cache: true intelligentRoutingSpec: enabled: true routingStrategy: prefixaware pdSpec: prefillSpec: replicas: 1 resources: requests: nvidia.com/gpu: "8" limits: nvidia.com/gpu: "8" decodingSpec: replicas: 1 resources: requests: nvidia.com/gpu: "8" limits: nvidia.com/gpu: "8" routingThreshold: 4096 worker: image: public.ecr.aws/deep-learning-containers/vllm:server-hyperpod-cuda-v1.1 args: - "--model" - "/opt/ml/model" - "--host" - "0.0.0.0" - "--port" - "8000" - "--tensor-parallel-size" - "8" - "--max-model-len" - "16384" - "--gpu-memory-utilization" - "0.75" modelInvocationPort: name: http containerPort: 8000 modelVolumeMount: name: model-weights mountPath: /opt/ml/model resources: requests: cpu: "96" memory: 1024Gi nvidia.com/gpu: "8" limits: cpu: "96" memory: 1024Gi nvidia.com/gpu: "8" environmentVariables: - name: HF_HOME value: /tmp/hf_home - name: PD_BUFFER_SIZE value: "8589934592" - name: LMCACHE_SAVE_DECODE_CACHE value: "False" - name: PYTHONHASHSEED value: "0"

Aplique o manifesto:

kubectl apply -f inference_endpoint_dpd_config.yaml

Verificar a implantação

A extração da imagem e o carregamento do modelo demoram vários minutos. Monitore o status do pod:

kubectl get pods -A \ | grep -E "prefill-|decode-|router"

Uma implantação saudável mostra:

NAMESPACE NAME READY STATUS RESTARTS AGE default prefill-dpd-test-XXXX 3/3 Running 0 7m default decode-dpd-test-XXXX 3/3 Running 0 7m hyperpod-inference-system dpd-test-router-XXXX 2/2 Running 0 7m

Cada pod de modelo tem 3 contêineres (trabalhador vLLM, proxy reverso Nginx, coletor). OpenTelemetry O pod do roteador tem 2 contêineres (roteador, OpenTelemetry coletor). Verifique o InferenceEndpointConfig status:

kubectl get inferenceendpointconfig dpd-test -n default \ -o jsonpath='{.status.conditions[0].message}{"\n"}'

Saída esperada: DPD prefill and decode deployments are ready

Verifique as funções do DPD

Confirme os relatórios do pré-preenchedor sender e os relatórios do decodificador. receiver Esse é o sinal de inicialização mais exigente: se os dois pods relatarem a mesma função ou nenhum imprimir a linha, o operador não conectou o DPD corretamente.

PREFILL_POD=$(kubectl get pod -n ${NAMESPACE} \ -l 'inference.sagemaker.aws.amazon.com/dpd-role=prefill' \ -o jsonpath='{.items[0].metadata.name}') DECODE_POD=$(kubectl get pod -n ${NAMESPACE} \ -l 'inference.sagemaker.aws.amazon.com/dpd-role=decode' \ -o jsonpath='{.items[0].metadata.name}') kubectl logs $PREFILL_POD -n ${NAMESPACE} -c prefill-${DEPLOYMENT_NAME} \ | grep -oE "'pd_role': '[a-z]+'" | sort -u kubectl logs $DECODE_POD -n ${NAMESPACE} -c decode-${DEPLOYMENT_NAME} \ | grep -oE "'pd_role': '[a-z]+'" | sort -u

Saída esperada:

'pd_role': 'sender' 'pd_role': 'receiver'

Invocar o endpoint

Quando o endpoint estiver pronto, envie um prompt curto e um longo para exercitar os dois caminhos de roteamento e, em seguida, verifique os registros para confirmar a transferência de KV pelo EFA.

PREFILL_POD=$(kubectl get pod -n ${NAMESPACE} \ -l 'inference.sagemaker.aws.amazon.com/dpd-role=prefill' \ -o jsonpath='{.items[0].metadata.name}') DECODE_POD=$(kubectl get pod -n ${NAMESPACE} \ -l 'inference.sagemaker.aws.amazon.com/dpd-role=decode' \ -o jsonpath='{.items[0].metadata.name}') ROUTER_POD=$(kubectl get pods -n hyperpod-inference-system -o name \ | grep -- "${DEPLOYMENT_NAME}-${NAMESPACE}-router" | head -1) ROUTER_URL=http://${DEPLOYMENT_NAME}-${NAMESPACE}-routing-service.hyperpod-inference-system.svc.cluster.local:443/v1/chat/completions

Aviso curto (abaixo do limite, direto para o decodificador)

Solicitações com menos tokens do que routingThreshold ignoram o pré-preenchedor e vão diretamente para o decodificador:

kubectl run curl-short --rm -it --image=curlimages/curl --restart=Never -- \ curl -s -k -X POST "$ROUTER_URL" \ -H "Content-Type: application/json" \ -d '{ "model": "/opt/ml/model", "messages": [{"role": "user", "content": "What is disaggregated prefill-decode in one sentence?"}], "max_tokens": 80, "temperature": 0.0 }'

Solicitação longa (excede o limite, caminho DPD)

Solicitações que excedem o limite são roteadas pelo pré-preenchedor para computação de cache KV e, em seguida, para o decodificador para geração de token:

kubectl run curl-long --rm -it --image=curlimages/curl --restart=Never -- sh -c ' LONG="" i=0; while [ $i -lt 600 ]; do LONG="${LONG}The quick brown fox jumps over the lazy dog. "; i=$((i+1)); done curl -s -k -X POST "'"$ROUTER_URL"'" \ -H "Content-Type: application/json" \ -d "{\"model\":\"/opt/ml/model\",\"messages\":[{\"role\":\"user\",\"content\":\"${LONG}\"}],\"max_tokens\":30,\"temperature\":0.0}" '

Verifique a transferência de KV

Depois de enviar um aviso longo, confirme se o cache KV foi transferido verificando os registros do decodificador:

kubectl logs $DECODE_POD -n ${NAMESPACE} -c decode-${DEPLOYMENT_NAME} \ | grep -E "Retrieved.*tokens.*throughput" | tail -2

Saída esperada (uma linha por classificação TP):

[Worker_TP5] [LMCache INFO] [req_id=cmpl-...] Retrieved 6035 out of 6035 required tokens (from 6035 total tokens). size: 0.2344 gb, cost 1.3304 ms, throughput: 176.1686 GB/s

Retrieved N out of N required tokenscom N > 0 confirma que o cache KV cruzou o canal NIXL com sucesso. Se você verRetrieved 0 out of N, o decodificador voltou para o recálculo local — veja. Problemas de implantação de pré-preenchimento e decodificação desagregados (DPD)

Você também pode verificar a decisão de roteamento nos registros do roteador:

kubectl logs $ROUTER_POD -n hyperpod-inference-system -c router-container --tail=20 \ | grep -E "Conditional routing"

Para o aviso longo, você deve ver:

[INFO] Conditional routing: estimated_tokens=6750, threshold=4096, disaggregate=True

Para o breve aviso:

[INFO] Conditional routing: estimated_tokens=12, threshold=4096, disaggregate=False
nota

Para invocar por meio de um endpoint de IA SageMaker AI, endpointName defina seu. InferenceEndpointConfig Se não endpointName estiver definido, nenhum endpoint SageMaker AI AI será criado e somente a invocação direta do ALB estará disponível.

Observabilidade

Ative as métricas metrics.enabled: true configurando seuInferenceEndpointConfig. As métricas do DPD estão disponíveis no painel de HyperPod inferência. Para obter mais informações, consulte Implementando a observabilidade de inferência em clusters HyperPod.

As seguintes DPD-specific métricas estão disponíveis:

DPD-specific métricas
Métrica Description
E2E TTFT Tempo total até o primeiro token (pré-preenchimento+transferência de KV +roteamento)
Preencha previamente o TTFT Prefiller-only latência
Fila de pré-preenchimento Número de solicitações aguardando o pré-preenchimento
Fila de decodificação Número de solicitações aguardando no decodificador
Tempo de pré-preenchimento Tempo gasto no cálculo do pré-preenchimento
Latência de decodificação Per-token latência de saída (TPOT)
Tempo de transferência de KV Hora de transferir o cache de KV do pré-preenchedor para o decodificador
Contagens de roteamento DPD Solicitações desagregadas versus solicitações alternativas (abaixo do limite)

Ajuste sua implantação de DPD

A tabela a seguir fornece uma referência rápida para ajustar o DPD com base nos sintomas que você observa em seu painel de métricas.

Referência de ajuste do DPD
Config O que ela faz Padrão Quando sintonizar
pdSpec.routingThreshold Tokens de entrada mínimos a serem roteados pelo pré-preenchedor. Solicitações abaixo desse limite vão diretamente para o decodificador. 4096 O padrão funciona bem para a maioria das cargas de trabalho. Configurá-lo como muito baixo aumenta o TTFT devido a transferências desnecessárias de KV em prompts curtos, enquanto configurá-lo como muito alto limita a melhoria do TPOT, pois menos solicitações seguem o caminho do DPD.
pdSpec.prefillSpec.replicas Número de cápsulas de pré-enchimento. 1 Aumente a escala se a profundidade da fila de pré-preenchimento for alta para melhorar o TTFT de pré-preenchimento.
PD_BUFFER_SIZE Buffer de GPU do decodificador para transferências de KV recebidas (por classificação). 8 GiB contém aproximadamente 35 K-token transferências em voo (6) para 70 B a TP=8. "8589934592"(8 GiB) Aumente para lidar com mais transferências simultâneas de KV. Diminua se você tiver problemas de memória. Ao aumentar, talvez seja necessário diminuir --gpu-memory-utilization o decodificador para liberar memória da GPU para o buffer maior.
--gpu-memory-utilization Fração da memória da GPU que o vLLM usa para pesos, ativações e cache de KV. 0.75 Aumente para obter mais espaço livre de cache em KV em entradas longas. Risco: OOM do pré-preenchedor porque o pré-preenchimento também precisa de memória para ativações. Teste com sua distribuição real do comprimento de entrada.
--max-num-seqs Máximo de sequências simultâneas por lote de trabalho. 16(pré-preenchedor), 32 (decodificador) Levante para uma melhor dosagem sob carga. Abaixe se atingir OOM no pré-preenchedor. Defina por função viapdSpec.{prefillSpec,decodingSpec}.args.
intelligentRoutingSpec.routingStrategy Como o roteador seleciona um pré-preenchedor quando existem várias réplicas. prefixaware Use roundrobin para distribuir uniformemente a carga em várias réplicas de pré-preenchimento. Use prefixaware ou kvaware com um único pré-preenchedor ou quando as solicitações compartilharem prefixos comuns (solicitações do sistema, histórico de bate-papo) para maximizar os acessos ao cache.

Teste com sua carga de trabalho real e a distribuição do comprimento de entrada.

Para aplicar as alterações de configuração, edite seu YAML de implantação e reaplique:

kubectl apply -f inference_endpoint_dpd_config.yaml

Limitações conhecidas

  • O DPD é recomendado para modelos densos com 70B ou mais parâmetros. Modelos e Mixture-of-Experts modelos menores normalmente não se beneficiam da desagregação.

  • A versão atual oferece suporte a uma única implantação de decodificação por endpoint. O suporte para várias implantações de decodificação está planejado para uma versão futura.

  • O desempenho é validado em até 64 solicitações simultâneas no ml.p5.48xlarge com o Llama 3.3 70B.

  • Para reverter de uma implantação de DPD para uma implantação colocalizada padrão, aplique uma nova sem. InferenceEndpointConfig pdSpec

Para solucionar problemas de implantações de DPD, consulte. Problemas de implantação de pré-preenchimento e decodificação desagregados (DPD)