Otimizar os endpoints de Servir de Modelos para produção

Aprenda a otimizar o desempenho dos endpoints de Model Serving para cargas de produção que exigem alta taxa de transferência, baixa latência e desempenho confiável.

As estratégias de otimização dividem-se em três categorias:

Quando otimizar o seu endpoint

Considere otimizar o seu endpoint de Model Serving quando encontrar algum dos seguintes cenários:

  • Elevado volume de consultas: A sua aplicação envia mais de 50 mil consultas por segundo (QPS) para um único endpoint
  • Requisitos de latência: A sua aplicação requer tempos de resposta inferiores a 100ms
  • Gargalos de escalabilidade: Os endpoints experienciam filas ou devolvem erros HTTP 429 durante picos de tráfego
  • Otimização de custos: Quer reduzir os custos de serviço mantendo as metas de desempenho
  • Preparação para produção: Está a preparar-se para passar do desenvolvimento para as cargas de produção

Otimizações da infraestrutura

As otimizações da infraestrutura melhoram o encaminhamento da rede, o comportamento de escalabilidade e a capacidade de computação.

Otimização de rotas

A otimização de rotas proporciona a melhoria de infraestrutura mais significativa para cargas de trabalho de alto rendimento. Quando ativa a otimização de rotas num endpoint, o Databricks Model Serving melhora o caminho de rede para pedidos de inferência, resultando numa comunicação mais rápida e direta entre clientes e modelos.

Benefícios de desempenho:

Característica Limite padrão de endpoint Limite de endpoint otimizado por rotas
Consultas por segundo (QPS) por espaço de trabalho 200 50.000+ (contacte a Databricks para limites superiores)
Concorrência de clientes por espaço de trabalho 192-1024 (varia consoante a região) Sem limite explícito (limitado por concorrência provisionada)
Concurrência configurada de endpoints por entidade servida 1,024 1.024 (contacte a Databricks para limites mais elevados)

Quando usar a otimização de rotas:

  • Cargas de trabalho que requerem mais de 200 QPS
  • Aplicações com requisitos rigorosos de latência (sobrecarga inferior a 50ms)
  • Implementações em produção que servem múltiplos utilizadores simultâneos

Importante

A otimização de rotas está disponível apenas para endpoints de modelos de serviço personalizados. As APIs do Foundation Model e modelos externos não suportam otimização de rotas. Tokens OAuth são necessários para autenticação; Tokens de acesso pessoal não são suportados.

Consulte Otimização de rotas nos endpoints de serviço para instruções de configuração e Interrogar endpoints de serviço otimizados para rotas para obter detalhes sobre consultas.

Concorrência provisionada

A concorrência provisionada controla quantos pedidos simultâneos o seu endpoint pode processar. Configure a concorrência provisionada com base nos seus requisitos esperados de latência e QPS.

Diretrizes de configuração:

  • Concorrência mínima: Definido suficientemente alto para gerir tráfego base sem necessidade de fila
  • Concorrência máxima: Definida suficientemente alta para acomodar picos de tráfego enquanto se controla os custos
  • Autoescalonamento: Permitir o autoescalonamento para ajustar dinamicamente a capacidade com base na procura

Calcule a concorrência necessária:

Required Concurrency = Target QPS × Average Latency (seconds)

Por exemplo, se a sua meta for 100 QPS com uma latência média de 200ms:

Required Concurrency = 100 × 0.2 = 20

Use testes de carga para medir a latência real e determinar as definições ótimas de concorrência.

Tipos de instância

Escolha os tipos de instância com base nos requisitos de computação do seu modelo:

Tipo de instância Melhor para Trade-offs
CPU (Pequeno, Médio, Grande) Modelos leves, lógica de inferência simples Menor custo, mais lento para modelos que exigem muita computação
GPU (Pequeno, Médio, Grande) Modelos grandes, cálculos complexos, processamento de imagem/vídeo Custo mais elevado, desempenho ótimo para aprendizagem profunda

Sugestão

Comece com instâncias de CPU para desenvolvimento e testes. Mude para instâncias de GPU apenas se observar uma alta latência de inferência ou se o seu modelo exigir computação especializada (como operações de aprendizagem profunda).

Otimizações de modelos

As otimizações de modelos melhoram a velocidade de inferência e a eficiência dos recursos.

Tamanho e complexidade do modelo

Tamanho e Complexidade do Modelo: Modelos menores e menos complexos geralmente conduzem a tempos de inferência mais rápidos e a QPS mais elevados. Considere técnicas como quantização de modelos ou poda se o seu modelo for grande.

Criação de batches

Se a sua aplicação conseguir enviar múltiplos pedidos numa única chamada, ative o batching do lado do cliente. Isto pode reduzir significativamente a sobrecarga para cada previsão.

Otimização do pré-processamento e pós-processamento

Descarregar o pré-processamento e pós-processamento complexos dos endpoints de serviço para reduzir a carga na infraestrutura de inferência.

Otimizações do lado do cliente

As otimizações do lado do cliente melhoram a forma como as aplicações interagem com os endpoints de serviço.

Agrupamento de conexões

O pooling de conexões reutiliza ligações existentes em vez de criar novas ligações para cada pedido, reduzindo significativamente a sobrecarga.

  • Use o Databricks SDK, que implementa automaticamente as melhores práticas de pooling de ligações
  • Se usares clientes personalizados, implementa tu próprio o pooling de ligações.

Gestão de erros e estratégias de tentativas

Implemente uma gestão robusta de erros para gerir falhas temporárias de forma eficiente, especialmente durante eventos de autoescalabilidade ou interrupções de rede.

Otimização do tamanho da carga útil

Minimizar o tamanho da carga de dados de pedidos e respostas para reduzir o tempo de transferência na rede e melhorar o throughput.

Medir e melhorar o desempenho

Monitorização do desempenho

Monitorizar o desempenho dos endpoints usando as ferramentas fornecidas pelo Model Serving:

Métrico O que mede Target Ação em caso de excesso
Latência (P50, P90, P99) Tempo de resposta aos pedidos Depende da aplicação (tipicamente <100-500ms) Verifique se há filas e otimize o modelo ou o cliente
Rendimento (QPS) Pedidos concluídos por segundo Depende da carga de trabalho Permitir a otimização de rotas, aumentar a concorrência provisionada
Taxa de erro Percentagem de pedidos falhados <1% Revisar registos de serviço, verificar problemas de capacidade
Profundidade da fila Pedidos à espera de processamento 0 (sem fila) Aumentar a concorrência provisionada ou ativar o dimensionamento automático
Utilização de CPU/Memória Utilização de recursos <80% Aumentar o tipo de instância ou aumentar a concorrência

Consulte Monitorizar a qualidade do modelo e a saúde dos endpoints para orientações detalhadas de monitorização e Monitorizar e exportar métricas de saúde dos pontos finais de serviço para o Prometheus e o Datadog para exportar métricas para ferramentas de observabilidade.

Testes de carga

O teste de carga mede o desempenho dos pontos finais em condições de tráfego realistas e ajuda a:

  • Determinar as configurações ideais de concorrência provisionada
  • Identificar estrangulamentos de desempenho
  • Validar requisitos de latência e capacidade
  • Compreenda a relação entre a concorrência do cliente e a concorrência do servidor

Veja Testes de carga para servidores de endpoints.

Resolver problemas de desempenho comuns

Queuing

O Model Serving suporta o autoescalonamento para ajustar a capacidade com base nos padrões de tráfego. No entanto, súbitos picos de tráfego podem causar fila porque o escalonamento automático requer tempo para detetar o aumento da carga e provisionar capacidade adicional. Durante este período, os pedidos recebidos podem temporariamente exceder a capacidade disponível, fazendo com que os pedidos entrem em fila.

O enfileiramento ocorre quando a taxa de solicitação ou concorrência ultrapassa a capacidade atual de processamento do endpoint. Isto acontece tipicamente durante picos bruscos de tráfego, explosões de carga de trabalho ou quando o endpoint tem concorrência provisionada insuficiente. Os pontos de extremidade do Model Serving permitem filas temporárias para lidar com rajadas; no entanto, além de um limite estabelecido, o ponto de extremidade devolve erros HTTP 429 (Demasiados Pedidos) para proteger a estabilidade do sistema.

A fila aumenta a latência porque os pedidos em fila esperam antes de serem processados. Para minimizar as filas:

  • Defina a concorrência mínima provisionada suficientemente alta para lidar com tráfego base mais rajadas típicas
  • Permitir a otimização de rotas para limites de capacidade mais elevados
  • Implemente lógica de tentativa de repetição com backoff exponencial nas aplicações cliente.

Gargalos externos de APIs

Os modelos frequentemente chamam APIs externas para enriquecimento de dados, recuperação de funcionalidades ou outras tarefas durante a inferência. Estas dependências externas podem tornar-se gargalos de desempenho:

  • Latência: Meça o tempo de resposta de cada chamada de API externa. A alta latência nestas chamadas aumenta diretamente a latência global de serviço e reduz a largura de banda.
  • Limites de throughput: APIs externas podem impor limites de taxa ou restrições de capacidade. Ultrapassar estes limites pode causar estrangulamento, erros e degradação do desempenho.
  • Taxas de erro: Erros frequentes de APIs externas podem desencadear novas tentativas e aumentar a carga no endpoint de atendimento.
  • Cache: Implementar cache para dados frequentemente acedidos de APIs externas para reduzir o número de chamadas e melhorar os tempos de resposta.

Monitorize estes fatores para identificar gargalos e implementar otimizações direcionadas para cargas de trabalho de alto rendimento.

Chamadas bloqueantes em código de modelo nos endpoints da CPU

Em modelos personalizados de CPU que servem endpoints que usam workers do gevent, uma chamada que bloqueia em código compilado pode bloquear todos os pedidos desse worker, mesmo quando a chamada é executada numa thread em segundo plano. Para evitar isto, execute essas chamadas num pool separado de threads do SO.

Um worker gevent pode servir várias solicitações ao mesmo tempo. Threads Python que o seu código de modelo inicia, incluindo threading.Thread e concurrent.futures.ThreadPoolExecutor, executam como tarefas cooperativas de gevent no thread do trabalhador. Eles só comutam em chamadas que o gevent torna cooperativas, como I/O de socket, time.sleep, e locks, por isso a I/O de rede puramente Python, por exemplo com requests, funciona como esperado. Uma chamada que espera dentro de código compilado, como uma extensão em C, C++ ou Rust, alguns drivers de base de dados ou alguns SDKs, não muda de contexto, pelo que bloqueia o worker inteiro até este regressar. Um sintoma comum são picos de latência ou timeouts em muitos pedidos ao mesmo tempo que coincidem com um serviço lento a jusante.

Para verificar se o teu endpoint usa workers do gevent, regista no log gevent.monkey.is_module_patched("threading") quando o teu modelo carrega. Em endpoints que não usam gevent, uma chamada bloqueante atrasa apenas o pedido que a fez.

A função auxiliar seguinte executa uma chamada de função num pool dedicado de threads do sistema operativo quando o gevent está ativo, e chama diretamente essa função caso contrário:

_pool = None


def run_blocking(fn, *args, **kwargs):
    """Run fn on an OS thread when gevent is active. Otherwise, call fn directly."""
    try:
        from gevent import monkey
        from gevent.threadpool import ThreadPool
    except ImportError:
        return fn(*args, **kwargs)
    if not monkey.is_module_patched("threading"):
        return fn(*args, **kwargs)
    global _pool
    if _pool is None:
        _pool = ThreadPool(8)
    return _pool.apply(fn, args, kwargs)

Por exemplo, numa thread em segundo plano que escreve registos com um cliente SDK que espera em código compilado, chame run_blocking(client.write, record) em vez de client.write(record). Tenha em mente o seguinte:

  • fn é executado numa thread diferente da do chamador, e várias chamadas podem correr ao mesmo tempo. Use-o apenas com objetos thread-safe, crie objetos ligados a threads, como ligações de base de dados dentro de fn, e passe valores específicos do pedido como argumentos. contextvars e valores locais de thread que o chamador define não são visíveis dentro de fn. Chame run_blocking a partir de código Python que o seu modelo executa, não de um callback executado por uma biblioteca compilada na sua própria thread de execução.
  • Defina um timeout para a chamada de bloqueio. Uma thread de pool não pode ser cancelada, por isso uma chamada que nunca retorna mantém a sua thread. Quando todos os threads do pool estão ocupados, as novas chamadas aguardam por um thread livre.
  • O offloading só ajuda se a chamada compilada libertar o bloqueio global do interpretador Python (GIL) enquanto espera.
  • Se fn levantar uma exceção, run_blocking levanta-a no chamador.

Recursos adicionais