

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 用于推理的分解预填和解码 HyperPod
<a name="sagemaker-hyperpod-model-deployment-dpd"></a>

分解预填和解码 (DPD) 将 LLM 推理的两个阶段（预填和解码）分离到专用 GPU 池中，并使用远程直接内存访问 (RDMA) 通过 Elastic Fabric Adapter (EFA) Fabric Adapter 在它们之间传输键值 (KV) 缓存。 GPU-Direct 

当预填和解码在同一 GPU（同置）上运行时，单个长上下文请求可能会阻碍其他客户端的动态令牌流，从而增加负载下每个令牌的延迟。DPD 通过在一组 GPU 上运行计算绑定预填充，在另一组 GPU 上运行内存带宽限制的解码来消除这种干扰，从而在混合流量下产生更可预测的延迟，并允许您独立扩展每个阶段。

推理操作员负责编排，包括配置路由器、通过 LmCache 和 NIXL 将预填和解码 pod 连接在一起，以及与可观察性集成。 HyperPod 您可以通过在已用于推理端点的`InferenceEndpointConfig`资源中添加一个`pdSpec`部分来启用 DPD。

## 当 DPD 提供帮助时
<a name="sagemaker-hyperpod-model-deployment-dpd-when"></a>

当满足以下所有条件时，DPD 可提供最大的收益：
+ **大型密集模型** — 70B 以上的参数（例如 Llama 3.3 70B）。
+ **长输入** — 4,000 多个输入标记。 Inter-token 延迟 (ITL) 的改善随输入长度而变化，因为较长的预填充在一起时会导致更多的解码干扰。
+ **持续并发** — 每秒 2 个以上的请求。如果没有并发请求争夺同一 GPU，就没有什么可分解的。
+ **中等或长输出** — 256 个以上的输出标记。更多的输出代币意味着稳定的每代币延迟带来更多的累积收益。

如果您的工作负载输入较短、并发性较低或使用小型模型，则标准托管部署会更简单且性能良好。

## 先决条件
<a name="sagemaker-hyperpod-model-deployment-dpd-prereqs"></a>

在部署使用分解预填和解码的推理端点之前，您需要在本地开发环境中设置以下组件：
+ [Amazon 命令行界面 (Amazon CLI)](https://docs.amazonaws.cn/cli/latest/userguide/cli-chap-getting-started.html)
+ 通过 kubectl 访问您的 HyperPod 亚马逊 E [K](https://kubernetes.io/docs/tasks/tools/) S 集群
+ [Huggin](https://huggingface.co/) g Face 令牌，允许读取相应模型检查点。如果模型检查点已位于 Amazon S3 存储桶中，则无需执行此操作。
+ 包括 vlLM、lmCache、NVIDIA NIXL 和 EFA libfabric 提供商的工作映像。支持以下图像选项：
  + DLC：`public.ecr.aws/deep-learning-containers/vllm:server-hyperpod-cuda-v1.1`
  + lmCache：`lmcache/vllm-openai:v0.4.3`

  这两张图片都包括 lmCache 0.4.3、vlLM 0.19.0 和 NIXL 1.0.0。
+ HyperPod 已安装推理运算符 **3.2 或更高版本**。早期版本不支持 DPD。默认情况下，操作员安装在新创建的 HyperPod Amazon EKS 集群中。如果您打算使用现有集群，请按照中的安装说明进行操作[设置 HyperPod 集群以进行模型部署](sagemaker-hyperpod-model-deployment-setup.md)。验证您的版本：

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

**重要**  
分解预填和解码需要支持 GPU-Direct RDMA 的 EFA-capable 实例。支持以下实例类型：`ml.p5.48xlarge`、`ml.p5e.48xlarge`、`ml.p5en.48xlarge`、`ml.p6-b200.48xlarge`、`ml.p6-b300.48xlarge`。DPD 不支持其他实例类型。

## 部署 DPD 终端节点
<a name="sagemaker-hyperpod-model-deployment-dpd-deploy"></a>

大多数`InferenceEndpointConfig`字段都与非 DPD 端点共享并记录在中。[部署基础模型和自定义经过微调的模型](sagemaker-hyperpod-model-deployment-deploy.md)要启用 DPD，请在清单中添加以下部分。

### Prefill-Decode 规格：p `d` Spec
<a name="sagemaker-hyperpod-model-deployment-dpd-fields-pdspec"></a>

声明 prefill/decode 拓扑并指定参数。此字段的存在是端点分解的原因：操作员为预填和解码创建单独的部署，并通过路由器和 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`  
独立缩放预填和解码。

`resources`  
应用于角色的 pod 规范。 Top-level`worker.resources`对于 DPD 窗格，会被忽略；每个角色的值会被覆盖。

`routingThreshold`  
将请求路由到分解路径的令牌长度阈值。不符合此阈值的请求会绕过预填器，直接进入解码器。

`args`  
特定于该角色的 vlLM 标志。启动`worker.args`时合并到：已`worker.args`存在的标志将替换为每个角色的值；附加不存在的标志。

### `DPD 环境变量：环境变量`
<a name="sagemaker-hyperpod-model-deployment-dpd-fields-env"></a>

这些环境变量以相同的方式应用于预填器和解码器容器；没有每个角色的 env-var 字段。对于每个角色的行为，请`pdSpec.{prefillSpec,decodingSpec}.args`改用。

```
environmentVariables:
  - name: PD_BUFFER_SIZE
    value: "8589934592"
  - name: LMCACHE_SAVE_DECODE_CACHE
    value: "False"
  - name: PYTHONHASHSEED
    value: "0"
```

`PD_BUFFER_SIZE`(8 GiB)  
解码器上预留的 GPU 缓冲区用于传入的 KV 缓存传输，按等级大小调整。对于 TP=8 的 Llama 70B，每个令牌的 KV 缓存约为每个等级 40 KB，因此 6000 个令牌的提示每个等级占用大约 0.23 GB，8 GiB 可容纳大约 35 次此类飞行中传输。当缓冲区超出容量时，解码器会记录日志`Failed to allocate memory object, retrying...`，客户端会看到延迟峰值。`decodingSpec.replicas`如果需要，可以增加到 16/32 GiB 或缩放。

`LMCACHE_SAVE_DECODE_CACHE`: `"False"`  
禁用解码器上的冗余 L1 缓存。预填器是缓存命中率的真实来源。

`PYTHONHASHSEED`: `"0"`  
LmCache 使用 Python 的内置功能`hash()`来计算提示令牌缓存密钥。默认情况下，Python 会将每个进程的哈希种子随机化，因此相同的提示会在预填器和解码器上生成不同的按键，查找会失败。固定种子会使各吊舱的密钥一致。

### 配置路由策略
<a name="sagemaker-hyperpod-model-deployment-dpd-fields-routing"></a>

该`intelligentRoutingSpec`部分设置了 DPD 路由器用来为每个请求选择预填器的路由策略。路由器存在时`pdSpec`会自动创建；此部分是可选的，默认为`prefixaware`。

```
intelligentRoutingSpec:
  enabled: true
  routingStrategy: prefixaware
```

DPD 还可以与智能路由和 KV 缓存集成。有关更多信息，请参阅 [配置 KV 缓存和智能路由](sagemaker-hyperpod-model-deployment-caching-routing.md#sagemaker-hyperpod-model-deployment-deploy-ftm-cache-route)。

使用单个预填副本，所有策略都会路由到该副本。只有在以下情况下，该选择才会影响行为`prefillSpec.replicas > 1`：
+ 对于单个预填副本，当提示共享系统提示或聊天记录等常见前缀时，使用`prefixaware`（默认）最大限度地提高 KV 缓存命中率。
+ 对于多个预填副本，请使用在副本之间均匀分配负载，避免`roundrobin`对单个预填充器进行热点。

### 完整示例
<a name="sagemaker-hyperpod-model-deployment-dpd-deploy-example"></a>

以下清单在两个 ml.p5.48xlarge 实例（一个预填器，一个解码器）上部署 Llama 3.3 70B：

```
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"
```

应用清单：

```
kubectl apply -f inference_endpoint_dpd_config.yaml
```

## 验证部署
<a name="sagemaker-hyperpod-model-deployment-dpd-verify"></a>

图像拉取和模型加载需要几分钟。监控 pod 状态：

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

健康的部署显示：

```
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
```

每个模型 pod 有 3 个容器（vlLM 工作器、Nginx 反向代理、收集器）。 OpenTelemetry路由器 pod 有 2 个容器（路由器、 OpenTelemetry 收集器）。检查`InferenceEndpointConfig`状态：

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

预期输出：`DPD prefill and decode deployments are ready`

### 验证 DPD 角色
<a name="sagemaker-hyperpod-model-deployment-dpd-verify-roles"></a>

确认预填报告`sender`和解码器报告。`receiver`这是最具辨别力的启动信号——如果两个 pod 都报告相同的角色或者都没有打印线路，则操作员没有正确接线 DPD。

```
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
```

预期输出：

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

## 调用端点
<a name="sagemaker-hyperpod-model-deployment-dpd-invoke"></a>

端点准备就绪后，发送简短和长长的提示以使用两条路由路径，然后检查日志以确认通过 EFA 传输 KV。

```
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
```

### 短提示（低于阈值，直接进入解码器）
<a name="sagemaker-hyperpod-model-deployment-dpd-invoke-short"></a>

令牌数少于`routingThreshold`绕过预填器并直接进入解码器的请求：

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

### 长提示（超过阈值，DPD 路径）
<a name="sagemaker-hyperpod-model-deployment-dpd-invoke-long"></a>

超过阈值的请求通过预填器进行KV缓存计算，然后路由到解码器以生成令牌：

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

### 验证 KV 传输
<a name="sagemaker-hyperpod-model-deployment-dpd-invoke-verify-kv"></a>

发送长提示后，通过检查解码器日志来确认 KV 缓存已传输：

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

预期输出（每个 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 tokens`如果设置为 N > 0，则确认 KV 缓存成功穿过 NIXL 通道。如果你看到`Retrieved 0 out of N`，解码器又回到了本地重新计算——请参阅。[分解的预填和解码 (DPD) 部署问题](sagemaker-hyperpod-model-deployment-ts-dpd.md)

您还可以在路由器日志中验证路由决定：

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

对于长提示，你应该看到：

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

对于简短的提示：

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

**注意**  
要通过 SageMaker AI AI 终端节点进行调用，请在您的`endpointName`中进行设置`InferenceEndpointConfig`。如果未设置，`endpointName`则不会创建 SageMaker AI AI 终端节点，并且只能直接调用 ALB。

## 可观测性
<a name="sagemaker-hyperpod-model-deployment-dpd-observability"></a>

通过`metrics.enabled: true`在您的中设置来启用指标`InferenceEndpointConfig`。DPD 指标可在 HyperPod 推理仪表板中找到。有关更多信息，请参阅 [在集群上实现推理可观测性 HyperPod](sagemaker-hyperpod-model-deployment-observability.md)。

以下 DPD-specific 指标可用：


**DPD-specific 指标**  

| 指标 | 说明 | 
| --- | --- | 
| E2E TTFT | 获得第一个代币的总时间（预填\+KV 转账\+路由） | 
| 预先填充 TTFT | Prefiller-only 延迟 | 
| 预填队列 | 等待预填的请求数 | 
| 解码队列 | 在解码器上等待的请求数 | 
| 预填时间 | 花在预填计算上的时间 | 
| 解码延迟 | Per-token 输出延迟 (TPOT) | 
| KV 传输时间 | 是时候将 KV 缓存从预填器传输到解码器了 | 
| DPD 路由计数 | 分解请求与后备请求（低于阈值） | 

## 调整您的 DPD 部署
<a name="sagemaker-hyperpod-model-deployment-dpd-tuning"></a>

下表提供了根据您在指标控制面板中观察到的症状调整 DPD 的快速参考。


**DPD 调整参考**  

| Config | 作用 | 默认 | 何时调音 | 
| --- | --- | --- | --- | 
| pdSpec.routingThreshold | 通过预填器路由的最小输入令牌。低于此阈值的请求会直接进入解码器。 | 4096 | 默认设置适用于大多数工作负载。将其设置得太低会增加 TTFT，因为在短提示时会进行不必要的 KV 传输，而将其设置得太高会限制 TPOT 的改进，因为通过 DPD 路径的请求较少。 | 
| pdSpec.prefillSpec.replicas | 预填充舱的数量。 | 1 | 如果预填队列深度较高，则扩大规模，以改善预填充的 TTFT。 | 
| PD\_BUFFER\_SIZE | 用于传入 KV 传输的解码器 GPU 缓冲区（按等级）。在 TP=8 时，8 GiB 可容纳大约 35 次机上 6 次K-token 转移，价格为 70B。 | "8589934592"(8 GiB) | 增加以处理更多并发 KV 传输。如果发现内存问题，请减小。增加时，您可能需要降低--gpu-memory-utilization解码器的容量，以便为较大的缓冲区释放 GPU 内存。 | 
| --gpu-memory-utilization | vLLM 用于权重、激活和 KV 缓存的 GPU 内存的一部分。 | 0.75 | 增加长输入时可获得更多 KV 缓存余量。风险：预填充器 OOM，因为预填还需要内存才能激活。使用您的实际输入长度分布进行测试。 | 
| --max-num-seqs | 每个工作批次的最大并发序列数。 | 16（预填充器）、32（解码器） | 在负载下提高批处理效果更好。如果击中预填充器上的 OOM，则降低。通过pdSpec.{prefillSpec,decodingSpec}.args按角色设置。 | 
| intelligentRoutingSpec.routingStrategy | 当存在多个副本时，路由器如何选择预填器。 | prefixaware | roundrobin用于在多个预填器副本之间均匀分配负载。使用prefixaware或kvaware与单个前缀一起使用，或者当提示共享通用前缀（系统提示、聊天记录）时，可以最大限度地提高缓存命中率。 | 

使用您的实际工作负载和输入长度分布进行测试。

要应用配置更改，请编辑您的部署 YAML 并重新应用：

```
kubectl apply -f inference_endpoint_dpd_config.yaml
```

## 已知限制条件
<a name="sagemaker-hyperpod-model-deployment-dpd-limitations"></a>
+ 对于具有 70B 或更多参数的密集模型，建议使用 DPD。较小的模型和 Mixture-of-Experts 模型通常无法从分解中受益。
+ 当前版本支持每个端点进行单一解码部署。计划在 future 版本中支持多种解码部署。
+ 使用 Llama 3.3 70B 在 ml.p5.48xlarge 上验证了多达 64 个并发请求的性能。
+ 要从 DPD 部署恢复到标准托管部署，请应用不带托管的新`InferenceEndpointConfig`部署。`pdSpec`

要对 DPD 部署进行故障排除，请参阅[分解的预填和解码 (DPD) 部署问题](sagemaker-hyperpod-model-deployment-ts-dpd.md)。