Mesure et réglage des performances d'un système d'inférence TensorFlow à l'aide de Triton Inference Server et de Tesla T4

Ce tutoriel explique comment mesurer les performances du système d'inférence TensorFlow que vous avez créé dans la partie 2 de cette série et comment appliquer des réglages afin d'améliorer le débit du système. Il ne vise pas à fournir les données sur les performances d'un système particulier. Au lieu de cela, il offre des conseils généraux sur le processus de mesure des performances.

Le nombre réel de métriques de performance, telles que "Nombre total de requêtes par seconde (RPS)" et "Temps de réponse", diffèrent selon le modèle entraîné, les versions logicielles et les configurations matérielles.

Objectifs

  • Définissez l'objectif de performances et les statistiques.
  • Mesurez les performances de référence.
  • Effectuer une optimisation du graphique.
  • Mesurez la conversion FP16.
  • Mesurez la quantification INT8.
  • Ajuster le nombre d'instances.

Coûts

En plus d'utiliser le GPU NVIDIA T4, ce tutoriel utilise les composants facturables suivants de Google Cloud :

Une fois que vous avez terminé ce tutoriel, vous pouvez éviter de continuer à payer des frais en supprimant les ressources que vous avez créées. Pour en savoir plus, consultez la section Effectuer un nettoyage.

Avant de commencer

Avant de commencer ce tutoriel, vous devez finir de créer le système d'inférence en suivant la partie 2 de cette série. Pour ce tutoriel, vous devez utiliser les interfaces suivantes :

Dans le terminal SSH, définissez le répertoire actuel sur le sous-répertoire client :

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

Dans ce tutoriel, vous allez exécuter toutes les commandes à partir de ce répertoire.

Définir l'objectif de performances

Lorsque vous mesurez les performances des systèmes d'inférence, vous devez définir l'objectif de performances et les métriques de performances appropriées en fonction du cas d'utilisation du système. Par souci de simplicité, ce tutoriel suppose les objectifs de performances suivants :

  • 95% des requêtes reçoivent des réponses dans un délai de 100 ms.
  • Le débit total (requêtes par seconde) s'améliore sans dépasser l'objectif précédent.

En utilisant ces hypothèses, vous mesurez le débit des modèles ResNet-50 suivants avec différentes optimisations. Lorsqu'un client envoie des requêtes d'inférence, il spécifie le modèle en utilisant le nom du modèle figurant dans cette table. Vous appliquez également un réglage des paramètres pour améliorer le débit du modèle.

Nom du modèle Optimisation
original Modèle d'origine (sans optimisation avec TF-TRT)
tftrt_fp32 Optimisation du graphique
(taille du lot: 64, groupes d'instances: 1)
tftrt_fp16 Conversion en FP16 en plus de l'optimisation du graphique
(taille du lot: 64, groupes d'instances: 1)
tftrt_int8 Quantification avec INT8 en plus de l'optimisation du graphique
(taille du lot: 64, groupes d'instances: 1)
tftrt_int8_bs16_count4 Quantification avec INT8 en plus de l'optimisation du graphique
(taille du lot: 16, groupes d'instances: 4)

Mesurer les performances de référence

Vous allez commencer par utiliser TF-TRT comme base pour mesurer les performances du modèle d'origine non optimisé. Vous comparez les performances des autres modèles avec celles des modèles d'origine afin d'évaluer quantitativement l'amélioration des performances. Lorsque vous avez déployé Locust, il était déjà configuré pour envoyer des requêtes pour le modèle d'origine.

  1. Ouvrez la console Locust et vérifiez que le nombre de clients (appelés esclaves) est 10. Si le nombre est inférieur à 10, les clients démarrent toujours. Dans ce cas, attendez quelques minutes jusqu'à ce qu'il passe à 10.
  2. Définissez Nombre d'utilisateurs à simuler sur 3000 et Taux d'apparition sur 5.
  3. Augmentez le nombre d'utilisations simulées de 5 par seconde jusqu'à 3 000 en cliquant sur Start swarming (Démarrer le travail en essaim).

    Démarrer une nouvel essaim Locust.

  4. Cliquez sur Graphiques pour afficher les graphiques suivants.

    Démarrer une nouvel essaim Locust.

    Notez que si la valeur Total Requests per Second (Nombre total de requêtes par seconde) augmente de manière linéaire, la valeur Response Times (ms) (temps de réponse en ms) augmente de la même façon.

  5. Lorsque la valeur de 95% du temps de réponse dépasse 100 ms, cliquez sur Stop pour arrêter la simulation. Si vous déplacez le pointeur de la souris sur le graphique, vous pouvez vérifier le nombre de requêtes par seconde correspondant aux cas où la valeur de 95% des centiles de réponses a dépassé 100 ms.

    Dans la capture d'écran suivante, le nombre de requêtes par seconde est de 253,1.

    Graphique montrant 253,1 requêtes par seconde.

    Nous vous recommandons de répéter cette mesure plusieurs fois et de prendre en compte une moyenne pour tenir compte de la fluctuation.

  6. Redémarrez 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. Revenez à l'étape 1 pour répéter la mesure.

Optimiser les graphiques

Dans cette section, vous allez évaluer les performances du modèle tftrt_fp32 optimisé à l'aide du module TF-TRT pour l'optimisation du graphe. Il s'agit d'une optimisation courante qui est compatible avec la plupart des cartes de GPU NVIDIA.

  1. Redémarrez l'outil de test de charge. Utilisez la ressource configmap pour spécifier le modèle en tant que 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. Redémarrez le serveur Triton :

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

    Attendez quelques minutes jusqu'à ce que les processus du serveur soient prêts.

  3. Répétez la mesure des performances réalisée dans la section précédente.

    Dans les captures d'écran suivantes, le nombre de requêtes par seconde est de 381.

    Graphique montrant le temps de réponse avec les requêtes par seconde de 381.

    Graphique illustrant 381 requêtes par seconde.

    Ces images montrent l'amélioration des performances de l'optimisation du graphique TF-TRT.

Conversion au format FP16

Dans cette section, vous allez évaluer les performances du modèle tftrt_fp16, qui est optimisé avec TF-TRT pour l'optimisation du graphique et la conversion FP16. Il s'agit d'une optimisation disponible pour NVIDIA Tesla T4.

  1. Redémarrez l'outil de test de charge. Utilisez la ressource configmap pour spécifier le modèle en tant que 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. Redémarrez le serveur Triton :

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

    Attendez quelques minutes jusqu'à ce que les processus du serveur soient prêts.

  3. Répétez la mesure des performances réalisée dans la section précédente. Dans l'exemple suivant, le nombre de requêtes par seconde est de 1072,5.

    Graphique montrant le temps de réponse avec 1072,5 requêtes par seconde.

    Graphique montrant 1072,5 requêtes par seconde.

    Ces images montrent l'amélioration des performances de la conversion FP16 en plus de l'optimisation du graphique TF-TRT.

Quantification avec INT8

Dans cette section, vous allez évaluer les performances du modèle tftrt_int8, qui est optimisé avec TF-TRT pour l'optimisation du graphe et la quantification INT8. Cette optimisation est disponible pour NVIDIA Tesla T4.

  1. Redémarrez l'outil de test de charge. Utilisez la ressource configmap pour spécifier le modèle en tant que 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. Redémarrez le serveur Triton :

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

    Attendez quelques minutes jusqu'à ce que les processus du serveur soient prêts.

  3. Répétez la mesure des performances réalisée dans la section précédente.

    Dans les captures d'écran suivantes, le nombre de requêtes par seconde est de 1085,4.

    Graphique montrant le temps de réponse avec 1085,4 requêtes par seconde.

    Graphique montrant 1085,4 requêtes par seconde.

    Ce résultat est pratiquement identique à la conversion FP16. Le fait d'utiliser la quantification INT8 n'entraîne pas un avantage. En théorie, le GPU NVIDIA Tesla T4 peut gérer les modèles de quantification INT8 plus rapidement que les modèles de conversion FP16. Dans ce cas, le goulot d'étranglement peut être différent des performances des GPU. Vous pouvez le vérifier à partir des données d'utilisation du GPU suivantes dans le tableau de bord Grafana. Notez que l'utilisation est inférieure à 40%, ce qui signifie que le modèle ne peut pas utiliser pleinement les performances du GPU.

    Graphique illustrant l'utilisation des GPU inférieure à 40%.

    Comme le montre la section suivante, vous pourrez peut-être faciliter ce goulot d'étranglement en augmentant le nombre de groupes d'instances. Par exemple, augmentez le nombre de groupes d'instances de 1 à 4, puis réduisez la taille de lot de 64 à 16. Cette approche conserve le nombre total de requêtes traitées sur un seul GPU à 64.

Ajuster le nombre d'instances

Dans cette section, vous allez mesurer les performances du modèle tftrt_int8_bs16_count4. Ce modèle possède la même structure que tftrt_int8, mais vous modifiez la taille de lot et le nombre de groupes d'instances, comme décrit à la fin de la section précédente.

  1. Redémarrez Locust. Utilisez la ressource configmap pour spécifier le modèle en tant que tftrt_int8_bs16_count4. Dans le même temps, augmentez le nombre de pods clients Locust pour générer suffisamment de charges de travail afin de mesurer la limitation des performances du modèle.

    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. Redémarrez le serveur Triton :

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

    Attendez quelques minutes jusqu'à ce que les processus du serveur soient prêts.

  3. Répétez la mesure des performances réalisée dans la section précédente. Toutefois, dans le cas présent, définissez Taux d'apparition sur 15 car le temps nécessaire pour atteindre la limite de performance est long si vous définissezTaux d'apparition sur 5. Dans l'exemple suivant, le nombre de requêtes par seconde est de 2236,6.

    Graphique montrant le temps de réponse avec 2 236,6 requêtes par seconde.

    Graphique illustrant 2 236,6 requêtes par seconde.

    En ajustant le nombre d'instances, vous exécutez presque deux requêtes par seconde. Notez que l'utilisation du GPU a atteint environ 75% sur le tableau de bord Grafana.

    Graphique illustrant l'utilisation du GPU de 75%.

Procéder au scaling avec plusieurs nœuds

Lorsque vous effectuez un scaling avec plusieurs nœuds, vous mesurez les performances d'un pod unique. Étant donné que les processus d'inférence sont exécutés indépendamment sur différents pods de manière "sans partage", vous pouvez supposer que le débit total évoluera de manière linéaire avec le nombre de pods. Cette hypothèse s'applique tant qu'il n'existe pas de goulots d'étranglement, tels que la bande passante réseau entre les clients et les serveurs d'inférence.

Toutefois, il est important de comprendre comment les requêtes d'inférence sont équilibrées entre plusieurs serveurs d'inférence. Triton utilise le protocole gRPC pour établir une connexion TCP entre un client et un serveur. Étant donné que Triton réutilise la connexion établie pour envoyer plusieurs requêtes d'inférence, les requêtes d'un même client sont toujours envoyées au même serveur. Pour distribuer les requêtes sur plusieurs serveurs, vous devez utiliser plusieurs clients.

Nettoyer

Supprimer le projet

  1. Dans la console, accédez à la page Gérer les ressources.

    Accéder à la page Gérer les ressources

  2. Dans la liste des projets, sélectionnez le projet que vous souhaitez supprimer, puis cliquez sur Supprimer.
  3. Dans la boîte de dialogue, saisissez l'ID du projet, puis cliquez sur Arrêter pour supprimer le projet.

Étape suivante