Optimiser une application Go
Ce tutoriel vous explique comment déployer une application Go intentionnellement inefficace, configurée pour collecter des données de profil. À l'aide de l'interface de Profiler, vous allez afficher les données de profil et identifier les optimisations potentielles. Ensuite, vous allez modifier l'application, la déployer, puis évaluer l'impact de la modification.
Avant de commencer
- Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Enable the required API.
- Pour ouvrir Cloud Shell, cliquez sur Activer Cloud Shell dans la barre d'outils de Google Cloud Console :
Après quelques instants, une session Cloud Shell s'ouvre dans Google Cloud Console :
Exemple d'application
L'objectif principal est de maximiser le nombre de requêtes par seconde que le serveur peut traiter. Un objectif secondaire consistera à réduire l'utilisation de la mémoire en éliminant les allocations de mémoire inutiles.
Le serveur, qui utilise un framework gRPC, reçoit un mot ou une expression, puis renvoie le nombre de fois où l'élément apparaît dans les œuvres de Shakespeare.
Des tests de charge réalisés sur le serveur déterminent le nombre moyen de requêtes par seconde qu'il est en mesure de gérer. Pour chaque série de tests, un simulateur de client est appelé et invité à émettre 20 requêtes de manière séquentielle. À la fin d'une série, le nombre de requêtes envoyées par le simulateur de client, le temps écoulé et le nombre moyen de requêtes par seconde sont affichés.
Le code du serveur est intentionnellement inefficace.
Exécuter l'exemple d'application
Téléchargez et exécutez l'exemple d'application :
Dans Cloud Shell, exécutez les commandes suivantes :
git clone https://github.com/GoogleCloudPlatform/golang-samples.git cd golang-samples/profiler/shakesapp
Exécutez l'application en définissant la version sur
1
et le nombre de séries sur 15 :go run . -version 1 -num_rounds 15
Au bout d'une ou deux minutes, les données de profil s'affichent. Ces données se présentent comme suit :
Dans la capture d'écran, notez que le type de profil est défini sur
CPU time
(temps CPU). Cela indique que les données d'utilisation du processeur sont affichées dans le graphique de type "flamme".Vous trouverez ci-dessous un exemple de résultat imprimé dans Cloud Shell :
$ go run . -version 1 -num_rounds 15 2020/08/27 17:27:34 Simulating client requests, round 1 2020/08/27 17:27:34 Stackdriver Profiler Go Agent version: 20200618 2020/08/27 17:27:34 profiler has started 2020/08/27 17:27:34 creating a new profile via profiler service 2020/08/27 17:27:51 Simulated 20 requests in 17.3s, rate of 1.156069 reqs / sec 2020/08/27 17:27:51 Simulating client requests, round 2 2020/08/27 17:28:10 Simulated 20 requests in 19.02s, rate of 1.051525 reqs / sec 2020/08/27 17:28:10 Simulating client requests, round 3 2020/08/27 17:28:29 Simulated 20 requests in 18.71s, rate of 1.068947 reqs / sec ... 2020/08/27 17:44:32 Simulating client requests, round 14 2020/08/27 17:46:04 Simulated 20 requests in 1m32.23s, rate of 0.216849 reqs / sec 2020/08/27 17:46:04 Simulating client requests, round 15 2020/08/27 17:47:52 Simulated 20 requests in 1m48.03s, rate of 0.185134 reqs / sec
Dans Cloud Shell, le résultat affiche le temps écoulé pour chaque itération, ainsi que le taux de requêtes moyen. Une fois l'application démarrée, l'entrée "Simulated 20 requests in 17.3s, rate of 1.156069 reqs / sec" (20 requêtes simulées en 17,3 s, taux de 1,156069 requêtes/s) indique que le serveur exécute environ une requête par seconde. Dans la dernière série, l'entrée "Simulated 20 requests in 1m48.03s, rate of 0.185134 reqs / sec" (20 requêtes simulées en 1 min 48,03 s, taux de 0,185134 requête/s) indique que le serveur exécute environ une requête toutes les cinq secondes.
Utiliser des profils de temps CPU pour maximiser le nombre de requêtes par seconde
Une approche pour maximiser le nombre de requêtes par seconde consiste à identifier les méthodes occasionnant une utilisation intensive du processeur et à optimiser leurs mises en œuvre. Dans cette section, vous allez employer des profils de temps CPU pour identifier une méthode occasionnant une utilisation intensive du processeur dans le serveur.
Déterminer le temps CPU utilisé
Le cadre racine du graphique de type "flamme" indique le temps CPU total utilisé par l'application sur l'intervalle de collecte de 10 secondes :
Dans cet exemple, le service a utilisé 2.37 s
. Lorsque le système s'exécute sur un seul cœur, une utilisation du temps CPU de 2,37 secondes équivaut à 23,7 % d'utilisation du cœur. Pour en savoir plus, consultez la section Types de profilage disponibles.
Modifier l'application
Évaluer la modification
Pour évaluer la modification, procédez comme suit :
Exécutez l'application en définissant sa version sur
2
:go run . -version 2 -num_rounds 40
Une section ultérieure montre qu'avec l'optimisation, le temps nécessaire à l'exécution d'une série est nettement inférieur à celui de l'application non modifiée. Pour garantir que l'application s'exécute suffisamment longtemps pour collecter et importer des profils, le nombre de séries est plus élevé.
Attendez que l'application termine de s'exécuter, puis affichez les données de profil de cette version :
- Cliquez sur MAINTENANT pour charger les données de profil les plus récentes. Pour en savoir plus, consultez la section Période d'affichage.
- Dans le menu Version, sélectionnez 2.
À titre d'exemple, le graphique de type "flamme" se présente comme suit :
Dans cette figure, le cadre racine affiche la valeur 7.8 s
. Suite à la modification de la fonction de correspondance de chaîne, le temps CPU utilisé par l'application est passé de 2,37 secondes à 7,8 secondes. En d'autres termes, l'application qui utilisait auparavant 23,7 % d'un cœur de processeur en utilise désormais 78 %.
La largeur du cadre est une mesure proportionnelle du temps CPU utilisé. Dans cet exemple, la largeur du cadre pour GetMatchCount
indique que la fonction utilise environ 49 % de l'ensemble du temps CPU utilisé par l'application.
Dans le graphique de type "flamme" initial, ce même cadre représentait environ 72 % de la largeur du graphique.
Pour connaître le temps CPU utilisé exact, vous pouvez afficher l'info-bulle du cadre ou exploiter la liste des fonctions de focus :
Le résultat dans Cloud Shell indique que la version modifiée effectue environ 5,8 requêtes par seconde :
$ go run . -version 2 -num_rounds 40 2020/08/27 18:21:40 Simulating client requests, round 1 2020/08/27 18:21:40 Stackdriver Profiler Go Agent version: 20200618 2020/08/27 18:21:40 profiler has started 2020/08/27 18:21:40 creating a new profile via profiler service 2020/08/27 18:21:44 Simulated 20 requests in 3.67s, rate of 5.449591 reqs / sec 2020/08/27 18:21:44 Simulating client requests, round 2 2020/08/27 18:21:47 Simulated 20 requests in 3.72s, rate of 5.376344 reqs / sec 2020/08/27 18:21:47 Simulating client requests, round 3 2020/08/27 18:21:51 Simulated 20 requests in 3.58s, rate of 5.586592 reqs / sec ... 2020/08/27 18:23:51 Simulating client requests, round 39 2020/08/27 18:23:54 Simulated 20 requests in 3.46s, rate of 5.780347 reqs / sec 2020/08/27 18:23:54 Simulating client requests, round 40 2020/08/27 18:23:58 Simulated 20 requests in 3.4s, rate of 5.882353 reqs / sec
La modification mineure de l'application a eu deux effets différents :
Le nombre de requêtes est passé de moins d'une requête par seconde à 5,8 requêtes par seconde.
Le temps CPU par requête, calculé en divisant l'utilisation du processeur par le nombre de requêtes par seconde, est passé de 23,7 % à 13,4 %.
Notez que le temps CPU par requête a diminué même si le temps CPU utilisé est passé de 2,37 secondes (ce qui correspond à une utilisation de 23,7 % d'un cœur de processeur) à 7,8 secondes (soit 78 % d'utilisation d'un cœur de processeur).
Utiliser des profils de segments de mémoire alloués pour améliorer l'utilisation des ressources
Cette section explique comment utiliser les profils de segments de mémoire et les profils de segments de mémoire alloués pour identifier une méthode occasionnant une allocation intensive dans l'application :
Les profils de segments de mémoire indiquent la quantité de mémoire allouée dans le segment de mémoire du programme au moment de la collecte du profil.
Les profils de segments de mémoire alloués indiquent la quantité totale de mémoire allouée dans le segment de mémoire du programme durant l'intervalle où la collecte du profil a été effectuée. En divisant ces valeurs par 10 secondes (l'intervalle de collecte des profils), vous pouvez les interpréter en tant que taux d'allocation.
Activer la collecte des profils de segments de mémoire
Exécutez l'application en définissant sa version sur
3
et activez la collecte de profils de segments de mémoire et de profils de segments de mémoire alloués.go run . -version 3 -num_rounds 40 -heap -heap_alloc
Attendez que l'application termine de s'exécuter, puis affichez les données de profil de cette version :
- Cliquez sur MAINTENANT pour charger les données de profil les plus récentes.
- Dans le menu Version, sélectionnez 3.
- Dans le menu Type de profileur, sélectionnez Segment de mémoire alloué.
À titre d'exemple, le graphique de type "flamme" se présente comme suit :
Identifier le taux d'allocation de segments de mémoire
Le cadre racine affiche la quantité totale de segments de mémoire alloués pendant les 10 secondes où un profil a été collecté, calculée en moyenne sur tous les profils. Dans cet exemple, le cadre racine indique que, en moyenne, 1,535 Gio de mémoire a été alloué.
Modifier l'application
Évaluer la modification
Pour évaluer la modification, procédez comme suit :
Exécutez l'application en définissant sa version sur
4
:go run . -version 4 -num_rounds 60 -heap -heap_alloc
Attendez que l'application termine de s'exécuter, puis affichez les données de profil de cette version :
- Cliquez sur MAINTENANT pour charger les données de profil les plus récentes.
- Dans le menu Version, sélectionnez 4.
- Dans le menu Type de profileur, sélectionnez Segment de mémoire alloué.
Pour quantifier l'impact de la modification de
readFiles
sur le taux d'allocation de segments de mémoire, comparez les profils de segments de mémoire alloués pour la version 4 à ceux collectés pour la version 3 :L'info-bulle du cadre racine indique que, dans la version 4, la quantité moyenne de mémoire allouée lors de la collecte des profils a diminué de 1,301 Gio par rapport à la version 3. L'info-bulle de
readFiles.func1
indique une diminution de 1,045 Gio :Pour quantifier l'impact sur la récupération de mémoire, configurez une comparaison des profils de temps CPU. Dans la capture d'écran ci-dessous, un filtre est appliqué afin d'afficher les piles du récupérateur de mémoire Go
runtime.gcBgMarkWorker.*
. La capture d'écran montre que l'utilisation du processeur pour la récupération de mémoire est passée de 16,8 % à 4,97 %.Pour déterminer si la modification a eu un impact sur le nombre de requêtes par seconde traitées par l'application, affichez le résultat dans Cloud Shell. Dans cet exemple, la version 4 exécute jusqu'à 15 requêtes par seconde, ce qui est nettement supérieur aux 5,8 requêtes par seconde de la version 3 :
$ go run . -version 4 -num_rounds 60 -heap -heap_alloc 2020/08/27 21:51:42 Simulating client requests, round 1 2020/08/27 21:51:42 Stackdriver Profiler Go Agent version: 20200618 2020/08/27 21:51:42 profiler has started 2020/08/27 21:51:42 creating a new profile via profiler service 2020/08/27 21:51:44 Simulated 20 requests in 1.47s, rate of 13.605442 reqs / sec 2020/08/27 21:51:44 Simulating client requests, round 2 2020/08/27 21:51:45 Simulated 20 requests in 1.3s, rate of 15.384615 reqs / sec 2020/08/27 21:51:45 Simulating client requests, round 3 2020/08/27 21:51:46 Simulated 20 requests in 1.31s, rate of 15.267176 reqs / sec ...
L'augmentation du nombre de requêtes par seconde traitées par l'application peut s'expliquer par la réduction du temps consacré à la récupération de mémoire.
Vous pouvez consulter les profils de segments de mémoire pour mieux comprendre l'impact de la modification de
readFiles
. En comparant les profils de segments de mémoire entre les versions 3 et 4, on peut constater que l'utilisation des segments de mémoire est passée de 70,95 Mio à 18,47 Mio :
Résumé
Dans ce guide de démarrage rapide, vous avez pu identifier les optimisations potentielles à apporter à une application en vous basant sur le temps CPU et sur des profils de segments de mémoire alloués. Les objectifs consistaient à optimiser le nombre de requêtes par seconde et à éliminer les allocations inutiles.
À l'aide de profils de temps CPU, vous avez pu identifier une fonction occasionnant une utilisation intensive du processeur. Après l'application d'une modification simple, le taux de requêtes du serveur est passé d'environ 1 par seconde à 5,8 par seconde.
À l'aide de profils de segments de mémoire alloués, vous avez pu découvrir que la fonction
shakesapp/server.go
readFiles
avait un taux d'allocation élevé. Après l'optimisation dereadFiles
, le taux de requêtes du serveur est passé à 15 requêtes par seconde et la quantité moyenne de mémoire allouée lors des 10 secondes de collecte des profils a diminué de 1,301 Gio.
Étape suivante
Pour en savoir plus sur la collecte et l'envoi de profils à votre projet Google Cloud, consultez la section Collecter des profils.
Consultez nos ressources sur DevOps et découvrez notre programme de recherche.
- Profiler des applications Go
- Profiler des applications Java
- Profiler des applications Node.js
- Profiler des applications Python
- Profiler des applications s'exécutant en dehors de Google Cloud