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, du préremplissage et du décodage, sur des pools de GPU dédiés et transfère le cache clé-valeur (KV) entre eux via Elastic Fabric Adapter (EFA) à l'aide de l'accès direct à la mémoire à distance (RDMA). GPU-Direct
Lorsque le préremplissage et le décodage sont exécutés sur le même GPU (colocalisé), une seule demande contextuelle longue peut bloquer les flux de jetons en vol pour d'autres clients, augmentant ainsi la latence par jeton en cas de charge. DPR supprime 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 en cas de trafic mixte et vous permet d'adapter chaque phase indépendamment.
L'opérateur d'inférence gère l'orchestration, qui comprend le provisionnement du routeur, le câblage des modules de préremplissage et de décodage via LMCache et NIXL, et l'intégration à l'observabilité. HyperPod Vous pouvez activer DPR en ajoutant une pdSpec section à la même InferenceEndpointConfig ressource que vous utilisez déjà pour les points de terminaison d'inférence.
Quand DPR vous aide
La DPR offre le plus d'avantages lorsque toutes les conditions suivantes sont réunies :
-
Grands modèles denses — paramètres 70B+ (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) évolue en fonction de la longueur d'entrée, car des préremplissages plus longs provoquent davantage d'interférences de décodage en cas de colocalisation.
-
Simultanéité soutenue : plus de 2 demandes par seconde. Sans requêtes 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 plus d'avantages cumulés 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 performant.
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
-
https://huggingface.co/
Jeton Hugging Face qui permet un accès 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. DPR n'est pas pris en charge sur les versions précédentes. L'opérateur est installé par défaut dans les clusters HyperPod Amazon EKS récemment créés. Si vous avez l'intention d'utiliser un cluster existant, suivez les instructions d'installation figurant 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 prenant en charge le RDMA. GPU-Direct 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 DPR.
Déploiement d'un terminal DPR
La plupart InferenceEndpointConfig des champs sont partagés avec des terminaux non DPR et documentés dans. Déploiement de modèles de fondation et de modèles personnalisés et peaufinés Pour activer DPR, 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 permet de désagréger le point de terminaison : l'opérateur crée des déploiements distincts pour le préremplissage et le décodage et les connecte ensemble 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 pod du rôle. Top-level
worker.resourcesest ignoré pour les pods DPR ; 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 requêtes qui n'atteignent pas ce seuil contournent le préremplisseur et vont directement au décodeur.
args-
Indicateurs vLLM spécifiques à ce rôle. Fusionné
worker.argsau démarrage : les indicateurs déjà présentsworker.argssont remplacés par la valeur par rôle ; les indicateurs absents sont ajoutés.
Variables d'environnement DPD:Variables d'environnement
Ces variables d'environnement sont appliquées de la même manière aux conteneurs de préremplissage et de décodeur ; il n'existe pas de 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 Go)-
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 GiB contiennent environ 35 transferts en vol. Lorsque la mémoire tampon dépasse sa capacité, le décodeur enregistre
Failed to allocate memory object, retrying...et les clients constatent des pics de latence. Augmentez jusqu'à 16/32 GiB ou augmentez l'échelledecodingSpec.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 de vérité pour les 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 les recherches échouent. L'épinglage de la graine permet de faire correspondre les clés d'une gousse à l'autre.
Configuration de la stratégie de routage
La intelligentRoutingSpec section définit la stratégie de routage utilisée par le routeur DPR 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. prefixaware
intelligentRoutingSpec: enabled: true routingStrategy: prefixaware
DPR 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 un seul réplica de préremplissage, toutes les stratégies sont acheminées vers ce réplica. Le choix n'affecte le comportement que lorsque prefillSpec.replicas > 1 :
-
Pour une seule réplique de préremplissage, utilisez
prefixaware(valeur par défaut) pour maximiser le nombre d'accès au cache KV lorsque les invites partagent des préfixes communs, tels que les invites du système ou l'historique des discussions. -
Pour plusieurs répliques de préremplissage, utilisez-le
roundrobinpour répartir la charge uniformément entre les répliques et éviter de créer des points chauds sur 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. Surveiller l'état du module :
kubectl get pods -A \ | grep -E "prefill-|decode-|router"
Un déploiement en bonne santé 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 module modèle possède 3 conteneurs (vLLM worker, proxy inverse Nginx, collecteur). OpenTelemetry Le module 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 DPR
Confirmez les rapports de préremplissage sender et les rapports du décodeur. receiver Il s'agit du signal de démarrage le plus discriminant : si les deux pods indiquent le même rôle ou si aucun des deux n'imprime la ligne, l'opérateur n'a pas correctement câblé 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 invite courte et une longue pour 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
Invite courte (en dessous du seuil, directement vers le décodeur)
Les requêtes contenant moins de jetons routingThreshold contournent le préremplisseur et vont directement 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 }'
Invite longue (dépasse le seuil, chemin DPR)
Les requêtes 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 traversé le canal NIXL avec succès. Si vous voyezRetrieved 0 out of N, le décodeur est revenu au recalcul local — voir. Problèmes de déploiement du préremplissage et du décodage désagrégés (DPR)
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 invoquer via un point de terminaison SageMaker IA, définissez endpointName dans votreInferenceEndpointConfig. S'il n'endpointNameest pas défini, aucun point de terminaison SageMaker AI AI n'est créé et seule l'invocation directe d'ALB est disponible.
Observabilité
Activez les métriques en les metrics.enabled: true paramétrant dans votreInferenceEndpointConfig. Les métriques DPR sont disponibles dans le tableau de bord HyperPod d'inférence. Pour de plus amples informations, veuillez consulter Mise en œuvre de l'observabilité par inférence sur les clusters HyperPod.
Les DPD-specific indicateurs suivants sont disponibles :
| Métrique | Description |
|---|---|
| E2E TTFT | Temps total écoulé jusqu'au premier jeton (préremplissage + transfert KV + routage) |
| Préremplir 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 | Temps de transfert du cache KV du préremplisseur au décodeur |
| Chiffres de routage DPR | Demandes désagrégées ou demandes de secours (inférieures au seuil) |
Optimisez votre déploiement DPR
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 effectuer le réglage |
|---|---|---|---|
pdSpec.routingThreshold |
Nombre minimal de jetons d'entrée à acheminer via le préremplisseur. Les demandes inférieures à ce seuil sont directement transmises au décodeur. | 4096 |
La valeur par défaut fonctionne correctement 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 l'échelle si la profondeur de la file de préremplissage est élevée afin d'améliorer le TTFT de préremplissage. |
PD_BUFFER_SIZE |
Tampon GPU du décodeur pour les transferts KV entrants (par rang). 8 GiB contient environ 35 6 K-token transferts en vol pour 70B à TP=8. | "8589934592"(8 Go) |
Augmenter pour gérer un plus grand nombre de transferts KV simultanés. Diminuez si vous constatez des problèmes de mémoire. Lorsque vous augmentez, vous devrez peut-être baisser --gpu-memory-utilization le décodeur pour 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 pondérations, les activations et le cache KV. | 0.75 |
Augmentez pour augmenter la marge de cache KV sur les entrées longues. Risque : OOM du préremplissage 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 maximum de séquences simultanées par lot de travailleurs. | 16(préremplisseur), 32 (décodeur) |
Augmentez pour un meilleur dosage sous charge. Réduisez la valeur si vous appuyez sur OOM sur le préremplisseur. Paramétrer 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éremplisseur. 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 d'accès au 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 DPR est recommandé pour les modèles denses avec 70B ou plus de paramètres. 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 terminal. La prise en charge de déploiements de décodage multiples est prévue dans une prochaine version.
-
Les performances sont validées jusqu'à 64 requêtes simultanées sur ml.p5.48xlarge avec Llama 3.3 70B.
-
Pour revenir d'un déploiement DPR à un déploiement colocalisé standard, appliquez un nouveau déploiement sans.
InferenceEndpointConfigpdSpec
Pour résoudre les problèmes liés aux déploiements DPR, consultez. Problèmes de déploiement du préremplissage et du décodage désagrégés (DPR)