

 **Ajudar a melhorar esta página** 

Para contribuir com este guia de usuário, escolha o link **Editar esta página no GitHub**, disponível no painel direito de cada página.

# Acelerar o carregamento de modelos no Amazon EKS
<a name="ml-inference-fast-model-loading"></a>

Quando você implanta grandes modelo de linguagem (LLMs) no Amazon EKS, o tempo de carregamento do modelo afeta diretamente a rapidez com que os pods podem começar a atender às requisições de inferência. Isso é especialmente verdadeiro durante eventos de aumento vertical da escala, quando novos pods ou nós precisam carregar o modelo antes de lidar com o tráfego. A inicialização do modelo tem duas fases que você pode melhorar com cache de artefatos de compilação e ajuste:
+  **Carregamento de pesos**: transferência via streaming dos arquivos de pesos do modelo do Amazon S3 para a memória da GPU utilizando [Run:ai Model Streamer](https://github.com/run-ai/runai-model-streamer).
+  **torch.compile**: compilação do grafo de computação do modelo em kernels otimizados e fundidos do CUDA/Triton. Essa compilação é executada na primeira inicialização e pode afetar significativamente o tempo de início, dependendo do tamanho do modelo.

Este tópico mostra como você pode otimizar o desempenho do Run:ai Model Streamer e o armazenamento em cache do torch.compile para reduzir as duas fases do processo de carregamento do modelo. Para obter o procedimento completo de implantação do vLLM no Amazon EKS para inferência, consulte [Carregar e servir modelos no Amazon EKS](ml-inference-load-serve-model.md).

## Otimizar o caminho de rede do S3 no Modo Automático do EKS
<a name="model-loading-auto-mode-s3"></a>

Se você estiver executando no Modo Automático do EKS com nós de GPU em sub-redes privadas, recomendamos usar um [endpoint da VPC de gateway para S3]({aws-docs-url}/vpc/latest/privatelink/vpc-endpoints-s3.html) para otimizar o caminho de rede entre seus nós e o S3. Com o endpoint da VPC de gateway, o tráfego para o S3 permanece na rede da AWS e ignora totalmente o gateway NAT, portanto, não há limite de largura de banda compartilhada nem cobrança de processamento de dados NAT por GB. Sem o endpoint da VPC de gateway, quando o tráfego passa por um gateway NAT, este torna-se um gargalo compartilhado durante a expansão quando vários nós baixam o modelo ao mesmo tempo.

O Modo Automático do EKS geralmente coloca nós em sub-redes privadas, portanto, o tráfego para o S3 flui por meio de um gateway NAT por padrão. Um gateway NAT fornece até 100 Gbps de largura de banda e 55 mil conexões simultâneas por destino. No entanto, essa largura de banda é compartilhada entre todos os nós da sub-rede privada. Durante um evento de aumento vertical da escala, vários nós que baixam o modelo completo ao mesmo tempo disputam a mesma largura de banda do gateway NAT, o que pode desacelerar o carregamento dos pesos do modelo em todos eles.

## Ajuste de desempenho do Run:ai Model Streamer
<a name="model-loading-runai-model-streamer"></a>

Mecanismos de inferência como o vLLM e SGLang usam o Run:ai Model Streamer como um mecanismo alternativo para carregar pesos durante a inicialização da inferência.

Por padrão, o Run:ai Model Streamer usa configurações conservadoras de simultaneidade ao baixar arquivos de pesos de modelos do S3. Aumentar a simultaneidade do download e o tamanho do fragmento reduz o tempo de carregamento dos pesos dos modelos ao baixar mais dados em paralelo.

### Calcular a simultaneidade ideal
<a name="model-loading-calculate-concurrency"></a>

Calcule o valor de simultaneidade ideal como:

```
concurrency = ceil(total_model_size_gb / chunk_size_gb)
```

Substitua o valor de `concurrency` que você usa com base no tamanho do modelo e no tamanho do fragmento. Consulte a tabela a seguir para ver alguns exemplos. Por exemplo, com um modelo de 67 GB e um tamanho de fragmento de 4 GB: `ceil(67 / 4) = 17`.


| Tamanho do modelo | Tamanho do bloco | Simultaneidade | 
| --- | --- | --- | 
| 10 GB | 4 GB | 3 | 
| 67 GB | 4 GB | 17 | 
| 140 GB | 4 GB | 35 | 

### Aplicar a configuração
<a name="model-loading-apply-configuration"></a>

Adicione os seguintes argumentos e variáveis de ambiente à especificação do seu contêiner de inferência:
+  `--tensor-parallel-size`: o grau de paralelismo de tensores (TP) geralmente é o número mínimo de GPUs necessárias para comportar o modelo na memória da GPU com base no tamanho do modelo. Por exemplo, um modelo de 67 GB em um tipo de instância `p5.48xlarge` requer pelo menos 2 GPUs, então defina-o como `2`.
+  `concurrency` e `distributed` (em `--model-loader-extra-config`): defina `concurrency` como o valor calculado para seu modelo. Defina `distributed` como `true` somente ao usar o paralelismo de tensores (TP > 1). Quando habilitado, cada classificação de paralelismo de tensor transmite seu próprio fragmento de peso diretamente do Amazon S3, em vez de a classificação 0 carregar todos os pesos e transmiti-los para as outras classificações. Isso melhora significativamente o desempenho de carregamento para implantações com várias GPUs. Deixe sem definição (ou `false`) para TP=1, pois não há nenhum benefício nessa opção. Essa opção requer a arquitetura vLLM V1. É incompatível com `--enforce-eager`, o que força o caminho V0; usar os dois juntos causará um erro ou retornará silenciosamente ao carregamento não distribuído.
+  `RUNAI_STREAMER_CHUNK_BYTESIZE`: tamanho do bloco de 4 GB. Esse valor mostra de forma consistente o melhor desempenho em todas as avaliações comparativas. Blocos maiores reduzem o número de requisições do S3 e melhoram o throughput em instâncias de alta largura de banda.
+  `RUNAI_STREAMER_S3_REQUEST_TIMEOUT_MS`: o tempo limite por requisição em milissegundos. Permite uma nova tentativa mais rápida em respostas lentas do S3.
+  `RUNAI_STREAMER_S3_LOW_SPEED_LIMIT`: velocidade mínima de transferência em bytes por segundo antes que uma requisição seja considerada lenta e executada novamente.

```
containers:
- name: vllm-inference
  image: vllm/vllm-openai:v0.21.0
  command:
    - python3
    - -m
    - vllm.entrypoints.openai.api_server
  args:
  # ... your existing args ...
  - --model=s3://<MODEL_PATH>
  - --tensor-parallel-size={{2}}
  - --load-format=runai_streamer
  - --model-loader-extra-config={"concurrency":{{17}},"distributed":{{true}}}
  env:
  - name: RUNAI_STREAMER_CHUNK_BYTESIZE
    value: {{"4294967296"}}
  - name: RUNAI_STREAMER_S3_REQUEST_TIMEOUT_MS
    value: {{"3000"}}
  - name: RUNAI_STREAMER_S3_LOW_SPEED_LIMIT
    value: {{"1048576"}}
```

## Otimizar a inicialização a frio de torch.compile
<a name="model-loading-torch-compile-cold-start"></a>

A etapa `torch.compile` do processo de disponibilização de inferência traça o grafo computacional de um modelo (a sequência de operações matemáticas) e o compila em kernels CUDA/Triton fundidos otimizados. Ele não compila os pesos dos modelos, apenas as operações que os transformam.

Os mecanismos de inferência usam o torch.compile porque ele fornece melhorias significativas no throughput automaticamente, sem engenharia de kernel personalizada por arquitetura de modelo:
+  **Fusão de kernel**: várias pequenas operações (soma residual, normalização de camada, ativação) são fundidas em um único kernel, reduzindo as idas e vindas à memória da GPU.
+  **Menos inicializações de kernel**: uma camada de transformador reduz de aproximadamente 15 a 30 kernels CUDA individuais para cerca de 10 fundidos, diminuindo a sobrecarga da CPU a cada inicialização.
+  **Python removido do caminho dinâmico**: toda a passagem direta torna-se um plano de execução em C\+\+, eliminando a sobrecarga do interpretador Python entre as operações.
+  **Melhor compatibilidade com o CUDA Graph**: grafos estáticos compilados são capturados e reproduzidos com quase zero de sobrecarga de CPU.
+  **Otimização automática**: funciona para qualquer arquitetura de modelo. O throughput melhora de 5 a 30% em relação ao modo eager.

A tabela a seguir mostra os mecanismos comuns de serviço de inferência que usam o torch.compile.


| Mecanismo | uso de torch.compile | 
| --- | --- | 
|  [vLLM](https://docs.vllm.ai/en/latest/)  | Habilitado por padrão (arquitetura V1) | 
|  [SGLang](https://github.com/sgl-project/sglang)  | Opcional via `--enable-torch-compile`  | 
| TensorRT-LLM | O novo caminho é compatível com essa opção junto com a compilação do mecanismo legado | 

### O problema de inicialização a frio do torch.compile
<a name="model-loading-cold-start-problem"></a>

A desvantagem do torch.compile é que o primeiro pod de inferência deve compilar antes de poder atender às requisições. Essa compilação pode levar vários minutos, dependendo do tamanho do modelo. Os artefatos compilados são pequenos (\~15 MB para um modelo de 60 GB), mas aumentam significativamente o tempo de inicialização a frio. Como os artefatos são pequenos e determinísticos para uma configuração específica, você pode armazená-los em cache e reutilizá-los para eliminar a penalidade de inicialização a frio nas inicializações subsequentes de pods e nós.

Os artefatos consistem em:
+ Arquivos-fonte de kernel Triton gerados
+ Binários do kernel compilados (`.cubin`)
+ Estrutura de grafo que sequencia as chamadas do kernel

### A relação de custo-benefício da opção --enforce-eager
<a name="model-loading-enforce-eager-tradeoff"></a>

O vLLM habilita o `torch.compile` e a captura de grafos do CUDA por padrão. O sinalizador `--enforce-eager` desativa os dois e executa o modelo no modo eager, em que cada operação é executada imediatamente por meio do interpretador Python. Como o modo eager ignora a compilação e a captura de grafos, alguns guias de início rápido, incluindo a implantação base em [Carregar e servir modelos no Amazon EKS](ml-inference-load-serve-model.md), usam `--enforce-eager` para iniciar os pods mais rapidamente.

 O `--enforce-eager` é uma opção válida para depuração, implantações com restrição de memória ou arquiteturas de modelo que não são compiladas de forma limpa, e você pode usá-lo na produção por esses motivos. Embora isso possa mitigar a penalidade de inicialização a frio do `torch.compile`, recomendamos outras abordagens, como as detalhadas nas seções subsequentes desta página para manter o desempenho do runtime na produção.

Entenda a compensação entre o tempo de inicialização e o desempenho no runtime de `--enforce-eager` antes de implantar na produção.


| Aspecto | Com `--enforce-eager` (modo eager) | Padrão (torch.compile \+ grafos do CUDA) | 
| --- | --- | --- | 
| Horário de início | Rápido: sem etapa de compilação ou captura de grafos | Inicialização a frio lenta (o problema resolvido por [Armazenar em cache os artefatos do torch.compile no mesmo nó](#model-loading-torch-compile-same-node) e [Pré-aqueça o cache torch.compile em novos nós](#model-loading-torch-compile-new-nodes)) | 
| Throughput em estado estável | Referência | \~5 a 30% maior em relação à fusão do kernel | 
| Latência por token (lote pequeno) | Maior sobrecarga de lançamento da CPU | Muito mais baixo: os grafos do CUDA reproduzem os lançamentos do kernel como uma unidade | 
| Memória da GPU | Menor e mais previsível | Superior: a captura de grafos pré-aloca grupos de buffers | 
| Capacidade de depuração | Rastreamentos de pilha limpos por operação | Erros surgem dentro dos kernels gerados | 

## Armazenar em cache os artefatos do torch.compile no mesmo nó
<a name="model-loading-torch-compile-same-node"></a>

Essa técnica se aplica quando você usa um mecanismo de inferência compatível com o `torch.compile`, como o vLLM (habilitado por padrão) ou o SGLang (habilitado via `--enable-torch-compile`). Funciona somente quando `torch.compile` está ativo, ou seja, quando `--enforce-eager` **não** está configurado.

**Importante**  
Essa melhoria não terá efeito se `torch.compile` estiver desabilitado. No vLLM, o sinalizador `--enforce-eager` desabilita o `torch.compile` totalmente, portanto, nenhum artefato é compilado ou armazenado em cache. Se você acompanhou a implantação de base em [Carregar e servir modelos no Amazon EKS](ml-inference-load-serve-model.md) com `--enforce-eager`, o vLLM cria o diretório de cache, mas nunca grava nele. Remova `--enforce-eager` antes de aplicar essa técnica.

Quando um mecanismo de inferência compila o grafo computacional do modelo na primeira inicialização, você pode armazenar em cache os kernels otimizados resultantes no armazenamento local do nó. Os pods subsequentes no mesmo nó reutilizam os artefatos em cache e ignoram totalmente a etapa de compilação, o que pode reduzir significativamente o tempo de inicialização.

### Adicionar variáveis de ambiente de cache
<a name="model-loading-add-cache-env-vars"></a>

Adicione as variáveis de ambiente a seguir à especificação do seu contêiner de inferência para direcionar o torch.compile e o cache do Triton para um caminho de host persistente. Recomendamos usar o armazenamento de instâncias NVMe local do nó e não o volume raiz do Amazon Elastic Block Store (Amazon EBS) para o `hostPath`. Para ver exemplos, consulte a seção a seguir.

```
containers:
- name: vllm-inference
  env:
  # torch.compile cache
  - name: XDG_CACHE_HOME
    value: {{"/compile-cache"}}
  - name: TORCHINDUCTOR_CACHE_DIR
    value: {{"/compile-cache/inductor"}}
  - name: TRITON_CACHE_DIR
    value: {{"/compile-cache/triton"}}
  volumeMounts:
  - name: compile-cache
    mountPath: /compile-cache
volumes:
- name: compile-cache
  hostPath:
    path: {{/mnt/k8s-disks/0/compile-cache}}
    type: DirectoryOrCreate
```

### Defina o caminho do cache para o armazenamento de instâncias NVMe
<a name="model-loading-nvme-instance-store"></a>

Artefatos compilados e pesos via streaming se beneficiam do rápido armazenamento local. Em instâncias de GPU com armazenamento de instâncias NVMe (como instâncias da família G e da família P), o armazenamento de instâncias fornece aproximadamente 30 GB/s, em comparação com aproximadamente 1 GB/s para o volume raiz do Amazon EBS. Direcione o `hostPath` do cache para o ponto de montagem do NVMe para otimizar o throughput.

**Importante**  
O volume do `hostPath` usa `type: DirectoryOrCreate`. Se você apontar para um caminho que não seja apoiado pelo armazenamento de instâncias NVMe, o Kubernetes criará silenciosamente o diretório no volume raiz do Amazon EBS. O cache ainda funciona, mas você perde o benefício de desempenho do NVMe sem nenhum erro ou aviso.

O ponto de montagem do NVMe e a forma como você o habilita diferem entre o Modo Automático do EKS e os nós autogerenciados:


| Computação | Ponto de montagem do NVMe | Como habilitar o armazenamento de instâncias NVMe | 
| --- | --- | --- | 
| Modo Automático do EKS |  `/mnt/.ephemeral`  | Habilitado dinamicamente com base no armazenamento temporário solicitado. O Modo Automático do EKS formata e monta o armazenamento de instâncias NVMe como uma matriz RAID 0 quando a instância tem várias unidades NVMe somente quando o `ephemeralStorage.size` solicitado no NodeClass é menor que a capacidade NVMe disponível da instância. Se o `ephemeralStorage.size` requisitado for igual ou maior que a capacidade do NVMe, o Modo Automático do EKS não usará o armazenamento de instância e o caminho será apoiado pelo volume raiz do EBS. | 
| Karpenter autogerenciado |  `/mnt/k8s-disks/0`  | Defina `instanceStorePolicy: RAID0` no `EC2NodeClass` do Karpenter. Sem isso, o Karpenter ignora os volumes de armazenamento de instâncias e o caminho não é apoiado pelo NVMe. | 

Em Modo Automático do EKS, defina o `hostPath` como `/mnt/.ephemeral/compile-cache` na especificação do seu contêiner:

```
volumes:
- name: compile-cache
  hostPath:
    path: /mnt/.ephemeral/compile-cache
    type: DirectoryOrCreate
```

Para o Karpenter autogerenciado, defina o `hostPath` como `/mnt/k8s-disks/0/compile-cache` na especificação do seu contêiner:

```
volumes:
- name: compile-cache
  hostPath:
    path: /mnt/k8s-disks/0/compile-cache
    type: DirectoryOrCreate
```

### Exemplos de resultados
<a name="model-loading-same-node-results"></a>

1. O primeiro pod em um nó executa torch.compile e grava os kernels compilados em `/compile-cache` no host.

1. Os pods subsequentes no mesmo nó montam o cache existente e ignoram totalmente a compilação, o que reduz a inicialização a frio do torch.compile de \~50 a 80s para \~4 a 6s.

A tabela a seguir mostra a melhoria com o armazenamento em cache do mesmo nó para diferentes tamanhos de modelo:


| Tamanho do modelo | Primeiro pod torch.compile (sem cache) | Pods subsequentes torch.compile (mesmo nó) | 
| --- | --- | --- | 
| 60 GB | \~53s | \~6s | 
| 140 GB | \~60s | \~6s | 
| 640 GB | \~80s | \~6s | 

## Pré-aqueça o cache torch.compile em novos nós
<a name="model-loading-torch-compile-new-nodes"></a>

Essa técnica se aplica quando você está executando uma inferência de vários nós com GPUs homogêneas, paralelismo de tensores, modelos e versões do PyTorch em todos os nós. A técnica em [Armazenar em cache os artefatos do torch.compile no mesmo nó](#model-loading-torch-compile-same-node) foca o caso de um único nó, mas os novos nós adicionados durante eventos de aumento vertical da escala começam com um cache torch.compile vazio. Para reduzir ainda mais o tempo de inicialização a frio em nós recém-inicializados, você pode implementar um mecanismo de cache que armazena os artefatos torch.compile compilados no S3 e os pré-baixa para novos nós quando eles ingressam no cluster.

A abordagem geral é:

1. Depois que o primeiro pod compilar o modelo no primeiro nó, faça o upload dos artefatos torch.compile (aproximadamente 15 MB) para um bucket do S3.

1. Quando novos nós ingressarem no cluster, baixe os artefatos em cache para o armazenamento local do nó antes que os pods de inferência sejam agendados.

Por exemplo, você pode implementar um DaemonSet que seja executado em nós de GPU que empacota e faz upload do cache torch.compile para o S3. Ele também pode sincronizar esse cache com o armazenamento local do nó antes que os pods de inferência sejam agendados em novos nós.

### Considerações sobre o armazenamento em cache entre nós
<a name="model-loading-cross-node-considerations"></a>

Quando você armazena em cache artefatos torch.compile entre nós, os kernels compilados são válidos somente quando esses parâmetros coincidem entre o nó que gerou o cache e o nó que o consome. Seu mecanismo de armazenamento em cache deve considerar tudo isso. Uma incompatibilidade em qualquer parâmetro produz um cache inválido que força a recompilação ou causa erros de runtime. Sua ferramenta de armazenamento em cache deve diferenciar os artefatos por esses parâmetros, por exemplo, incorporando-os à chave de objeto do S3 ou à estrutura do diretório de cache.


| Parâmetro | Por que isso importa? | 
| --- | --- | 
| Tipo de GPU | Os kernels compilados são específicos da arquitetura da GPU (por exemplo, sm\_90 para H100 vs sm\_89 para L4). | 
| Paralelismo de tensores (TP) | Diferentes graus de TP produzem diferentes partições do grafo de computação. | 
| Modelo | Cada arquitetura e tamanho do modelo é compilado em diferentes kernels. | 
| Versão PyTorch | As estruturas internas dos compiladores torch.compile e Triton podem introduzir alterações incompatíveis entre versões. | 

### Exemplos de resultados
<a name="model-loading-cross-node-results"></a>

Em testes direcionais com pré-aquecimento de cache entre nós (Qwen3-6-35B-A3B, 67 GB, 2x GPU com TP=2 em p5.48xlarge), o primeiro pod em nós recém-escalados alcançou o mesmo tempo de inicialização dos pods subsequentes em um nó já aquecido:


| Cenário | Primeiro pod | Segundo pod | 
| --- | --- | --- | 
| Sem cache entre nós (novo nó) | 65s | 16s | 
| Com cache entre nós (novo nó) | 16s | 16s | 

## Exemplo de implantação
<a name="model-loading-deployment-example"></a>

O exemplo a seguir combina o ajuste de desempenho do Run:AI Model Streamer e um cache torch.compile em um único manifesto de implantação do vLLM. Substitua os valores dos espaços reservados pela sua própria configuração:
+  `serviceAccountName`: conta de serviço com um perfil do IAM que tem acesso de leitura do Amazon S3 ao seu bucket de modelos.
+  `nodeSelector` (`karpenter.sh/nodepool`): o nome do seu grupo de nós da GPU (por exemplo, `gpu-nodepool-g6e-12xlarge`).
+  `--model`: o caminho do Amazon S3 até os pesos do seu modelo.
+  `--model-loader-extra-config`: defina `concurrency` com base no tamanho do seu modelo: `ceil(total_model_size_gb / chunk_size_gb)`. Por exemplo, um modelo de 67 GB com um tamanho de bloco de 4 GB oferece `ceil(67 / 4) = 17`.
+  `--tensor-parallel-size`: defina o grau de paralelismo de tensores (TP) para o número mínimo de GPUs necessárias para comportar o modelo na memória.
+  `hostPath` `path`: ponto de montagem do armazenamento de instâncias NVMe para nós autogerenciados com o Karpenter. No Modo Automático do EKS, use `/mnt/.ephemeral/compile-cache`. Para mais detalhes, consulte [Armazenar em cache os artefatos do torch.compile no mesmo nó](#model-loading-torch-compile-same-node).

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-inference
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: vllm-inference
  template:
    metadata:
      labels:
        app: vllm-inference
    spec:
      serviceAccountName: <SERVICE_ACCOUNT_NAME>
      nodeSelector:
        karpenter.sh/nodepool: <GPU_NODEPOOL>
      tolerations:
        - key: nvidia.com/gpu
          operator: Exists
          effect: NoSchedule
      containers:
        - name: vllm
          image: vllm/vllm-openai:v0.21.0
          command:
            - python3
            - -m
            - vllm.entrypoints.openai.api_server
          args:
            - --model=s3://<BUCKET_NAME>/<MODEL_PATH>
            - --load-format=runai_streamer
            - --model-loader-extra-config={"concurrency":{{17}},"distributed":{{true}}}
            - --tensor-parallel-size={{2}}
            - --max-model-len=8192
            - --host=0.0.0.0
            - --port=8000
          ports:
            - containerPort: 8000
              name: http
          env:
            # Run:ai streamer tuning
            - name: RUNAI_STREAMER_CHUNK_BYTESIZE
              value: {{"4294967296"}}
            - name: RUNAI_STREAMER_S3_REQUEST_TIMEOUT_MS
              value: {{"3000"}}
            - name: RUNAI_STREAMER_S3_LOW_SPEED_LIMIT
              value: {{"1048576"}}
            # torch.compile cache
            - name: XDG_CACHE_HOME
              value: {{"/compile-cache"}}
            - name: TORCHINDUCTOR_CACHE_DIR
              value: {{"/compile-cache/inductor"}}
            - name: TRITON_CACHE_DIR
              value: {{"/compile-cache/triton"}}
          resources:
            requests:
              cpu: "12"
              memory: 80Gi
              nvidia.com/gpu: "2"
            limits:
              nvidia.com/gpu: "2"
          volumeMounts:
            - name: compile-cache
              mountPath: /compile-cache
          startupProbe:
            httpGet:
              path: /health
              port: 8000
            periodSeconds: 10
            failureThreshold: 60
            initialDelaySeconds: 30
          readinessProbe:
            httpGet:
              path: /health
              port: 8000
            periodSeconds: 5
            timeoutSeconds: 3
      volumes:
        - name: compile-cache
          hostPath:
            path: {{/mnt/k8s-disks/0/compile-cache}}
            type: DirectoryOrCreate
```