View a markdown version of this page

Préremplissage et décodage désagrégés pour inférence HyperPod - Amazon SageMaker AI

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Préremplissage et décodage désagrégés pour inférence HyperPod

Le préremplissage et le décodage désagrégés (DDP) séparent les deux phases de l'inférence LLM, à savoir le préremplissage et le décodage, sur des pools de GPU dédiés et transfère le cache clé-valeur (KV) entre elles via Elastic Fabric Adapter (EFA) à l'aide du Remote Direct Memory Access (RDMA). GPU-Direct

Lorsque le préremplissage et le décodage sont exécutés sur le même processeur graphique (colocalisés), une seule demande de contexte long peut bloquer les flux de jetons en cours pour d'autres clients, augmentant ainsi le temps de latence par jeton sous charge. DDP élimine ces interférences en exécutant un préremplissage lié au calcul sur un ensemble de GPU et un décodage lié à la bande passante mémoire sur un autre, ce qui produit une latence plus prévisible dans un trafic mixte et vous permet de dimensionner chaque phase indépendamment.

L'opérateur d'inférence gère l'orchestration, qui comprend le provisionnement du routeur, le câblage des pods de préremplissage et de décodage ensemble via LMCache et NIXL, et l'intégration avec l'observabilité. HyperPod Vous pouvez activer DDP en ajoutant une pdSpec section à la même InferenceEndpointConfig ressource que celle que vous utilisez déjà pour les points de terminaison d'inférence.

Quand DDP vous aide

DDP offre le plus d'avantages lorsque toutes les conditions suivantes sont réunies :

  • Grands modèles denses : plus de 70 milliards de paramètres (par exemple, Llama 3.3 70B).

  • Entrées longues — Plus de 4 000 jetons d'entrée. Inter-token L'amélioration de la latence (ITL) varie en fonction de la longueur d'entrée, car les préremplissages plus longs provoquent davantage d'interférences de décodage en cas de colocation.

  • Concurrence soutenue : 2 requêtes ou plus par seconde. Sans demandes simultanées en concurrence pour le même GPU, il n'y a rien à désagréger.

  • Sorties modérées ou longues : plus de 256 jetons de sortie. Un plus grand nombre de jetons de sortie signifie un avantage cumulatif accru grâce à une latence stable par jeton.

Si votre charge de travail comporte des entrées courtes, une faible simultanéité ou utilise de petits modèles, un déploiement colocalisé standard est plus simple et fonctionne bien.

Conditions préalables

Avant de déployer des points de terminaison d'inférence qui utilisent le préremplissage et le décodage désagrégés, vous devez configurer les composants suivants dans votre environnement de développement local :

  • AWS Interface de ligne de commande (AWS CLI)

  • Accès à votre cluster HyperPod Amazon EKS via kubectl

  • Jeton Hugging Face qui permet d'accéder en lecture au point de contrôle du modèle correspondant. Cela n'est pas obligatoire si le point de contrôle du modèle se trouve déjà dans un compartiment Amazon S3.

  • Une image de travail qui inclut VLLM, LMCache, NVIDIA NIXL et le fournisseur EFA libfabric. Les options d'image suivantes sont prises en charge :

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

    • Cache LMC : lmcache/vllm-openai:v0.4.3

    Les deux images incluent LMCache 0.4.3, vLLM 0.19.0 et NIXL 1.0.0.

  • HyperPod La version 3.2 ou ultérieure de l'opérateur d'inférence est installée. DDP n'est pas pris en charge sur les versions antérieures. L'opérateur est installé par défaut dans les clusters HyperPod Amazon EKS nouvellement créés. Si vous avez l'intention d'utiliser un cluster existant, suivez les instructions d'installation indiquées dansConfiguration de vos HyperPod clusters pour le déploiement de modèles. Vérifiez votre version :

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

Le préremplissage et le décodage désagrégés nécessitent des EFA-capable instances compatibles GPU-Direct RDMA. Les types d'instances suivants sont pris en charge : ml.p5.48xlargeml.p5e.48xlarge,ml.p5en.48xlarge,ml.p6-b200.48xlarge,ml.p6-b300.48xlarge. Les autres types d'instances ne sont pas pris en charge pour DDP.

Déploiement d'un point de terminaison DDP

La plupart des InferenceEndpointConfig champs sont partagés avec des points de terminaison autres que DDP et sont documentés dans. Déploiement de modèles de fondation et de modèles personnalisés et peaufinés Pour activer DDP, ajoutez les sections suivantes à votre manifeste.

Prefill-Decode Spécification : PDSpec

Déclare la prefill/decode topologie et spécifie les arguments. La présence de ce champ est à l'origine de la désagrégation du terminal : l'opérateur crée des déploiements distincts pour le préremplissage et le décodage et les connecte via le routeur et le backend LMCache DP.

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

Dimensionnez, préremplissez et décodez indépendamment.

resources

Appliqué à la spécification du module du rôle. Top-levelworker.resourcesest ignoré pour les pods DDP ; les valeurs par rôle sont remplacées.

routingThreshold

Seuil de longueur du jeton qui achemine les demandes vers le chemin désagrégé. Les demandes qui n'atteignent pas ce seuil contournent le préremplisseur et sont directement transmises au décodeur.

args

Indicateurs vLLM spécifiques à ce rôle. Fusionné worker.args au démarrage : les drapeaux déjà présents worker.args sont remplacés par la valeur par rôle ; les drapeaux absents sont ajoutés.

Variables d'environnement DPD:Variables d'environnement

Ces variables d'environnement sont appliquées de manière identique aux conteneurs du préremplisseur et du décodeur ; il n'existe aucun champ env-var par rôle. Pour le comportement par rôle, utilisez pdSpec.{prefillSpec,decodingSpec}.args plutôt.

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

Mémoire tampon GPU réservée sur le décodeur pour les transferts de cache KV entrants, dimensionnée par rang. Pour Llama 70B à TP=8, le cache KV de chaque jeton est d'environ 40 Ko par rang, donc une invite de 6 000 jetons occupe environ 0,23 Go par rang et 8 Go contient environ 35 transferts en vol. Lorsque la mémoire tampon dépasse sa capacité, le décodeur enregistre des pics de latence Failed to allocate memory object, retrying... et les clients constatent des pics de latence. Augmentez jusqu'à 16/32 GiB ou augmentez decodingSpec.replicas si nécessaire.

LMCACHE_SAVE_DECODE_CACHE: "False"

Désactive la mise en cache L1 redondante sur le décodeur. Le préremplisseur est la source fiable des accès au cache.

PYTHONHASHSEED: "0"

LMCache utilise la fonction intégrée de Python hash() pour calculer les clés de cache à jeton d'invite. Python randomise cette graine de hachage par processus par défaut, de sorte que des instructions identiques produisent des clés différentes sur le préremplisseur et le décodeur et que les recherches ne sont pas effectuées. En épinglant la graine, les clés s'accordent entre les gousses.

Configuration de la stratégie de routage

La intelligentRoutingSpec section définit la stratégie de routage utilisée par le routeur DDP pour sélectionner un préremplisseur pour chaque demande. Le routeur est créé automatiquement lorsqu'il pdSpec est présent ; cette section est facultative et est définie par défaut surprefixaware.

intelligentRoutingSpec: enabled: true routingStrategy: prefixaware

DDP peut également être intégré au routage intelligent et à la mise en cache KV. Pour de plus amples informations, veuillez consulter Configuration de la mise en cache KV et du routage intelligent.

Avec une seule réplique de préremplissage, toutes les stratégies sont acheminées vers cette réplique. Le choix n'affecte le comportement que lorsque prefillSpec.replicas > 1 :

  • Pour une réplique de préremplissage unique, utilisez prefixaware (par défaut) pour maximiser les accès au cache KV lorsque les invites partagent des préfixes communs tels que les invites système ou l'historique des discussions.

  • Pour plusieurs répliques de préremplissage, utilisez-le roundrobin pour répartir la charge uniformément sur les répliques et éviter de détecter un seul préremplisseur.

Exemple complet

Le manifeste suivant déploie Llama 3.3 70B sur deux instances ml.p5.48xlarge (un préremplisseur, un décodeur) :

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"

Appliquez le manifeste :

kubectl apply -f inference_endpoint_dpd_config.yaml

Vérification du déploiement

L'extraction de l'image et le chargement du modèle prennent plusieurs minutes. État du module de surveillance :

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

Un déploiement sain montre que :

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

Chaque modèle de pod possède 3 conteneurs (vLLM worker, Nginx reverse proxy, collector). OpenTelemetry Le module de routeur comporte 2 conteneurs (routeur, OpenTelemetry collecteur). Vérifiez le InferenceEndpointConfig statut :

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

Résultat attendu : DPD prefill and decode deployments are ready

Vérifier les rôles DDP

Confirmez les rapports du préremplisseur sender et ceux du décodeur. receiver Il s'agit du signal de démarrage le plus discriminant : si les deux modules signalent le même rôle ou si aucun des deux n'imprime la ligne, l'opérateur n'a pas correctement câblé le DDP.

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

Sortie attendue :

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

Appeler le point de terminaison

Une fois que le terminal est prêt, envoyez une courte et une longue invite à utiliser les deux chemins de routage, puis consultez les journaux pour confirmer le transfert KV via 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

Courte invite (en dessous du seuil, directement au décodeur)

Les demandes contenant moins de jetons routingThreshold contournent le préremplisseur et sont directement envoyées au décodeur :

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 }'

Demande longue (dépasse le seuil, chemin DDP)

Les demandes qui dépassent le seuil passent par le préremplisseur pour le calcul du cache KV, puis vers le décodeur pour la génération de jetons :

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}" '

Vérifier le transfert KV

Après avoir envoyé une longue invite, confirmez que le cache KV a été transféré en consultant les journaux du décodeur :

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

Résultat attendu (une ligne par rang 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 tokensavec N > 0 confirme que le cache KV a correctement traversé le canal NIXL. Si vous voyezRetrieved 0 out of N, le décodeur est revenu au recalcul local — voir. Problèmes liés au déploiement du préremplissage et du décodage désagrégés (DDP)

Vous pouvez également vérifier la décision de routage dans les journaux du routeur :

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

Pour la longue invite, vous devriez voir :

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

Pour le bref message :

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

Pour appeler via un point de terminaison SageMaker AI AI, définissez endpointName votreInferenceEndpointConfig. Si endpointName ce n'est pas le cas, aucun point de terminaison SageMaker AI AI n'est créé et seule l'invocation directe d'ALB est disponible.

Observabilité

Activez les métriques metrics.enabled: true en définissant votreInferenceEndpointConfig. Les métriques DDP sont disponibles dans le tableau de bord HyperPod d'inférence. Pour de plus amples informations, veuillez consulter Implémentation de l'observabilité des inférences sur les clusters HyperPod.

Les DPD-specific métriques suivantes sont disponibles :

DPD-specific métriques
Métrique Description
E2E TTFT Temps total jusqu'au premier jeton (préremplissage + transfert KV + routage)
Pré-remplir le TTFT Prefiller-only latence
File d'attente de préremplissage Nombre de demandes en attente de préremplissage
File d'attente de décodage Nombre de requêtes en attente sur le décodeur
Temps de préremplissage Temps consacré au calcul du préremplissage
Latence de décodage Per-token latence de sortie (TPOT)
Temps de transfert KV Il est temps de transférer le cache KV du préremplisseur au décodeur
Nombre de routages DDP Demandes désagrégées et demandes de secours (inférieures au seuil)

Ajustez votre déploiement de DDP

Le tableau suivant fournit une référence rapide pour ajuster DDP en fonction des symptômes que vous observez dans votre tableau de bord de statistiques.

Référence de réglage DDP
Config Ce qu'il fait Par défaut Quand régler
pdSpec.routingThreshold Nombre minimum de jetons d'entrée à acheminer via le préremplisseur. Les demandes inférieures à ce seuil sont directement envoyées au décodeur. 4096 La valeur par défaut fonctionne bien pour la plupart des charges de travail. Une valeur trop faible augmente le TTFT en raison de transferts KV inutiles sur de courtes instructions, tandis qu'une valeur trop élevée limite l'amélioration du TPOT, car moins de demandes empruntent le chemin DDP.
pdSpec.prefillSpec.replicas Nombre de dosettes préremplies. 1 Augmentez si la profondeur de la file d'attente de préremplissage est élevée afin d'améliorer le TTFT de préremplissage.
PD_BUFFER_SIZE Mémoire tampon GPU du décodeur pour les transferts KV entrants (par rang). 8 GiB contient environ 35 6 K-token transferts en vol pour 70 B à TP=8. "8589934592"(8 GiB) Augmentez pour gérer davantage de transferts KV simultanés. Diminuez si vous constatez des problèmes de mémoire. Lorsque vous augmentez, vous devrez peut-être abaisser --gpu-memory-utilization le décodeur afin de libérer de la mémoire GPU pour la plus grande mémoire tampon.
--gpu-memory-utilization Fraction de la mémoire GPU utilisée par VLLm pour les poids, les activations et le cache en kilovolts. 0.75 Augmentez pour augmenter la marge de cache en kilo-volts sur les entrées longues. Risque : préremplissage OOM car le préremplissage nécessite également de la mémoire pour les activations. Testez avec votre distribution de longueur d'entrée réelle.
--max-num-seqs Nombre maximal de séquences simultanées par lot de travail. 16(préremplisseur), 32 (décodeur) Augmentez pour un meilleur dosage sous charge. Baissez si vous appuyez sur OOM sur le préremplisseur. Défini par rôle viapdSpec.{prefillSpec,decodingSpec}.args.
intelligentRoutingSpec.routingStrategy Comment le routeur sélectionne un préremplisseur lorsqu'il existe plusieurs répliques. prefixaware roundrobinÀ utiliser pour répartir uniformément la charge sur plusieurs répliques de préremplisseurs. Utilisez prefixaware ou kvaware avec un seul préremplisseur ou lorsque les invites partagent des préfixes communs (instructions système, historique des discussions) afin de maximiser le nombre de visites dans le cache.

Testez avec votre charge de travail réelle et la distribution de la longueur d'entrée.

Pour appliquer les modifications de configuration, modifiez le code YAML de votre déploiement et réappliquez :

kubectl apply -f inference_endpoint_dpd_config.yaml

Limitations connues

  • Le DDP est recommandé pour les modèles denses avec 70 milliards de paramètres ou plus. Les modèles plus petits et Mixture-of-Experts les modèles ne bénéficient généralement pas de la désagrégation.

  • La version actuelle prend en charge un seul déploiement de décodage par point de terminaison. Support pour plusieurs déploiements de décodage est prévu pour une future version.

  • Les performances sont validées jusqu'à 64 requêtes simultanées sur ml.p5.48xlarge avec Llama 3.3 70B.

  • Pour passer d'un déploiement DDP à un déploiement colocalisé standard, appliquez un nouveau déploiement sans. InferenceEndpointConfig pdSpec

Pour résoudre les problèmes liés aux déploiements de DDP, consultez. Problèmes liés au déploiement du préremplissage et du décodage désagrégés (DDP)