Como medir e ajustar o desempenho de um sistema de inferência do TensorFlow com o servidor de inferência Triton e o Tesla T4

Neste tutorial, mostramos como medir o desempenho do sistema de inferência do TensorFlow criado na parte 2 desta série e aplicar o ajuste de parâmetros para melhorar a capacidade do sistema. O tutorial não fornece os dados de desempenho de um sistema específico. Em vez disso, ele oferece uma orientação geral sobre o processo de medição de desempenho.

Os números concretos de métricas de desempenho, como "Total de solicitações por segundo (RPS)" e "Tempo de resposta", diferem dependendo do modelo treinado, das versões de software e das configurações de hardware.

Objetivos

  • Definir o objetivo e as métricas de desempenho.
  • Avaliar o desempenho da linha de base.
  • Realizar a otimização do gráfico.
  • Avaliar a conversão do FP16.
  • Medir a quantização INT8.
  • Ajustar o número de instâncias.

Custos

Além de usar a GPU NVIDIA T4, este tutorial usa os seguintes componentes faturáveis do Google Cloud:

Ao concluir este tutorial, exclua os recursos criados para evitar o faturamento contínuo. Para mais informações, consulte Limpeza.

Antes de começar

Antes de começar o tutorial, conclua a criação do sistema de inferência seguindo a parte 2 desta série. Use as interfaces a seguir para trabalhar neste tutorial:

No terminal SSH, defina o diretório atual para o subdiretório client:

cd $HOME/gke-tensorflow-inference-system-tutorial/client

Neste tutorial, todos os comandos deste diretório são executados.

Como definir o objetivo de desempenho

Ao medir o desempenho de sistemas de inferência, você precisa definir o objetivo de desempenho e as métricas de desempenho adequadas de acordo com o caso de uso do sistema. Para simplificar, este tutorial pressupõe os seguintes objetivos de desempenho:

  • 95% das solicitações recebem respostas em 100 ms.
  • A capacidade total (solicitações por segundo) melhora sem interromper o objetivo anterior.

Usando essas suposições, você mede a capacidade dos modelos ResNet-50 a seguir com diferentes otimizações. Quando um cliente envia solicitações de inferência, ele especifica o modelo usando o nome do modelo na tabela. O ajuste de parâmetro também é aplicado para melhorar a capacidade do modelo.

Nome do modelo Otimização
original Modelo original (sem otimização com TF-TRT)
tftrt_fp32 Otimização de gráficos
(tamanho do lote: 64, grupos de instâncias: 1)
tftrt_fp16 Conversão para FP16, além da otimização do gráfico
(tamanho do lote: 64, grupos de instâncias: 1)
tftrt_int8 Quantização com INT8, além da otimização do gráfico
(tamanho do lote: 64, grupos de instâncias: 1)
tftrt_int8_bs16_count4 Quantização com INT8, além da otimização do gráfico
(tamanho do lote: 16 grupos de instâncias: 4)

Como avaliar o desempenho da linha de base

Comece usando o TF-TRT como um valor de referência para avaliar o desempenho do modelo original e não otimizado. Você compara o desempenho de outros modelos com o original para avaliar quantitativamente a melhoria de desempenho. Quando você implantou o Locust, ele já estava configurado para enviar solicitações para o modelo original.

  1. Abra o console do Locust e confirme se o número de clientes (chamados de escravos) é 10. Se o número for menor que 10, os clientes ainda estão iniciando. Nesse caso, espere alguns minutos até se tornar 10.
  2. Defina o Número de usuários para simular como 3000 e o Hatch rate como 5.
  3. Aumente o número de usos simulados em cinco por segundo até alcançar 3.000 clicando em Iniciar aquecimento.

    Iniciando um novo swarm do Locust.

  4. Clique em Gráficos para mostrar os seguintes gráficos.

    Iniciando um novo swarm do Locust.

    Observe que, embora o valor de Total de solicitações por segundo aumente linearmente, o valor de Tempos de resposta (ms) aumenta de acordo.

  5. Quando o valor de percentual de 95% dos tempos de resposta exceder 100 ms, clique em Interromper para interromper a simulação. Se você mover o ponteiro do mouse sobre o gráfico, poderá verificar o número de solicitações por segundo correspondentes quando o valor de 95% do tempo de resposta excedeu 100 ms.

    Na captura de tela a seguir, o número de solicitações por segundo é 253.1.

    Gráfico mostrando 25,1 solicitações por segundo.

    Recomendamos repetir essa medição várias vezes e calcular uma média para considerar a flutuação.

  6. Reinicie o Locust:

    kubectl delete -f deployment_master.yaml -n locust
    kubectl delete -f deployment_slave.yaml -n locust
    kubectl apply -f deployment_master.yaml -n locust
    kubectl apply -f deployment_slave.yaml -n locust
    
  7. Volte para a etapa 1 para repetir a medição.

Como otimizar gráficos

Nesta seção, você mede o desempenho do modelo tftrt_fp32 que é otimizado usando o TF-TRT para a otimização do gráfico. Essa é uma otimização comum compatível com a maioria das placas da GPU NVIDIA.

  1. Reinicie a ferramenta de teste de carga. Use o recurso configmap para especificar o modelo como tftrt_fp32.

    kubectl delete configmap locust-config -n locust
    kubectl create configmap locust-config \
        --from-literal model=tftrt_fp32 \
        --from-literal saddr=${TRITON_IP} \
        --from-literal rps=10 -n locust
    kubectl delete -f deployment_master.yaml -n locust
    kubectl delete -f deployment_slave.yaml -n locust
    kubectl apply -f deployment_master.yaml -n locust
    kubectl apply -f deployment_slave.yaml -n locust
    
  2. Reinicie o servidor Triton:

    kubectl scale deployment/inference-server --replicas=0
    kubectl scale deployment/inference-server --replicas=1
    

    Aguarde alguns minutos até que os processos do servidor estejam prontos.

  3. Repita a medição de desempenho que você usou na seção anterior.

    Nas capturas de tela a seguir, o número de solicitações por segundo é 381.

    Gráfico que mostra o tempo de resposta com as solicitações por segundo de 381.

    Gráfico mostrando 381 solicitações por segundo.

    Essas imagens mostram a melhoria no desempenho da otimização do gráfico TF-TRT.

Como converter para FP16

Nesta seção, você mede o desempenho do modelo tftrt_fp16 que está otimizado com o TF-TRT para otimização de gráficos e conversão de FP16. Essa é uma otimização disponível para NVIDIA Tesla T4.

  1. Reinicie a ferramenta de teste de carga. Use o recurso configmap para especificar o modelo como tftrt_fp16.

    kubectl delete configmap locust-config -n locust
    kubectl create configmap locust-config \
        --from-literal model=tftrt_fp16 \
        --from-literal saddr=${TRITON_IP} \
        --from-literal rps=10 -n locust
    kubectl delete -f deployment_master.yaml -n locust
    kubectl delete -f deployment_slave.yaml -n locust
    kubectl apply -f deployment_master.yaml -n locust
    kubectl apply -f deployment_slave.yaml -n locust
    
  2. Reinicie o servidor Triton:

    kubectl scale deployment/inference-server --replicas=0
    kubectl scale deployment/inference-server --replicas=1
    

    Aguarde alguns minutos até que os processos do servidor estejam prontos.

  3. Repita a medição de desempenho que você usou na seção anterior. No exemplo a seguir, o número de solicitações por segundo é 1072,5.

    Gráfico que mostra o tempo de resposta com 107,5 solicitações por segundo.

    Gráfico mostrando 107,5 solicitações por segundo.

    Essas imagens mostram a melhoria no desempenho da conversão do FP16, além da otimização do gráfico TF-TRT.

Quantização com INT8

Nesta seção, você mede o desempenho do modelo tftrt_int8 otimizado para TF-TRT para otimização de gráficos e quantização INT8. Essa otimização está disponível para NVIDIA Tesla T4.

  1. Reinicie a ferramenta de teste de carga. Use o recurso configmap para especificar o modelo como tftrt_int8.

    kubectl delete configmap locust-config -n locust
    kubectl create configmap locust-config \
        --from-literal model=tftrt_int8 \
        --from-literal saddr=${TRITON_IP} \
        --from-literal rps=10 -n locust
    kubectl delete -f deployment_master.yaml -n locust
    kubectl delete -f deployment_slave.yaml -n locust
    kubectl apply -f deployment_master.yaml -n locust
    kubectl apply -f deployment_slave.yaml -n locust
    
  2. Reinicie o servidor Triton:

    kubectl scale deployment/inference-server --replicas=0
    kubectl scale deployment/inference-server --replicas=1
    

    Aguarde alguns minutos até que os processos do servidor estejam prontos.

  3. Repita a medição de desempenho que você usou na seção anterior.

    Nas capturas de tela a seguir, o número de solicitações por segundo é de 5.84.4.

    Gráfico que mostra o tempo de resposta com 1.084. Solicitações por segundo.

    Gráfico mostrando 1085.4 solicitações por segundo.

    Esse resultado é quase igual à conversão do FP16. Você não observa uma vantagem em usar a quantização INT8. Teoricamente, a GPU NVIDIA Tesla T4 pode processar modelos de quantização INT8 mais rapidamente do que os modelos de conversão FP16. Nesse caso, pode haver um gargalo diferente do desempenho da GPU. É possível confirmar isso usando os seguintes dados de utilização da GPU no painel do Grafana. Observe que a utilização é inferior a 40%, o que significa que o modelo não pode utilizar totalmente o desempenho da GPU.

    Gráfico mostrando a utilização da GPU de menos de 40%.

    Como na próxima seção, é possível aliviar esse gargalo aumentando o número de grupos de instâncias. Por exemplo, aumente o número de grupos de instâncias de 1 a 4 e diminua o tamanho do lote de 64 para 16. Essa abordagem mantém o número total de solicitações processadas em uma única GPU em 64.

Como ajustar o número de instâncias

Nesta seção, você mede o desempenho do modelo tftrt_int8_bs16_count4. Esse modelo tem a mesma estrutura que tftrt_int8, mas você altera o tamanho do lote e o número de grupos de instâncias, conforme descrito no final da seção anterior.

  1. Reinicie o Locust. Use o recurso configmap para especificar o modelo como tftrt_int8_bs16_count4. Ao mesmo tempo, aumente o número de pods de cliente do Locust para gerar cargas de trabalho suficientes para medir a limitação de desempenho do modelo.

    kubectl delete configmap locust-config -n locust
    kubectl create configmap locust-config \
        --from-literal model=tftrt_int8_bs16_count4 \
        --from-literal saddr=${TRITON_IP} \
        --from-literal rps=10 -n locust
    kubectl delete -f deployment_master.yaml -n locust
    kubectl delete -f deployment_slave.yaml -n locust
    kubectl apply -f deployment_master.yaml -n locust
    kubectl apply -f deployment_slave.yaml -n locust
    kubectl scale deployment/locust-slave --replicas=20 -n locust
    
  2. Reinicie o servidor Triton:

    kubectl scale deployment/inference-server --replicas=0
    kubectl scale deployment/inference-server --replicas=1
    

    Aguarde alguns minutos até que os processos do servidor estejam prontos.

  3. Repita a medição de desempenho que você usou na seção anterior. Neste caso, defina Hatch rate (taxa de geração) como 15 porque demora muito para atingir o limite de desempenho da taxa de geração como 5. No exemplo a seguir, o número de solicitações por segundo é 2236,6.

    Gráfico que mostra o tempo de resposta com 2236,6 solicitações por segundo.

    Gráfico mostrando 2236.6 solicitações por segundo.

    Ao ajustar o número de instâncias, você quase duplica solicitações por segundo. Observe que a utilização de GPU atingiu cerca de 75% no painel do Grafana.

    Gráfico mostrando a utilização de GPU de 75%.

Como escalonar com vários nós

Ao escalonar com vários nós, você mede o desempenho de um único pod. Como os processos de inferência são executados de maneira independente em pods diferentes de forma não compartilhada, suponha que a capacidade total seja escalonada linearmente com o número de pods. Essa suposição se aplica, contanto que não haja gargalos, como a largura de banda da rede entre os clientes e os servidores de inferência.

No entanto, é importante entender como as solicitações de inferência são balanceadas entre vários servidores de inferência. O Triton usa o protocolo gRPC para estabelecer uma conexão TCP entre um cliente e um servidor. Como o Triton reutiliza a conexão estabelecida para enviar várias solicitações de inferência, as solicitações de um único cliente são sempre enviadas para o mesmo servidor. Para distribuir solicitações para vários servidores, é preciso usar vários clientes.

Limpeza

Exclua o projeto

  1. No console, acesse a página Gerenciar recursos.

    Acessar "Gerenciar recursos"

  2. Na lista de projetos, selecione o projeto que você quer excluir e clique em Excluir .
  3. Na caixa de diálogo, digite o ID do projeto e clique em Encerrar para excluí-lo.

A seguir