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 :
-
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-level
worker.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.argsau démarrage : les drapeaux déjà présentsworker.argssont 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 augmentezdecodingSpec.replicassi 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
roundrobinpour 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 :
| 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.
| 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.
InferenceEndpointConfigpdSpec
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)