On pourrait utiliser un profileur, mais pourquoi ne pas arrêter le programme?

Si quelque chose fait en sorte qu’un programme à un seul thread prenne, disons, 10 fois plus longtemps, vous pouvez lancer un profileur sur celui-ci. Vous pouvez aussi simplement l’arrêter avec un bouton “pause”, et vous verrez exactement ce qu’il fait.

Même si ce n’est que 10% de moins que ce qu’il devrait être, si vous l’arrêtez plus de fois, vous le verrez bientôt faire des choses inutiles. Habituellement, le problème est un appel de fonction quelque part au milieu de la stack qui n’est pas vraiment nécessaire. Cela ne mesure pas le problème, mais il est certain qu’il le trouve.

Edit: Les objections supposent principalement que vous prenez seulement 1 échantillon. Si vous êtes sérieux, prenez 10. Toute ligne de code causant un certain pourcentage de gaspillage, comme 40%, apparaîtra sur la stack sur cette fraction d’échantillons, en moyenne. Les goulots d’étranglement (dans le code à un seul thread) ne peuvent pas en cacher.

EDIT: Pour montrer ce que je veux dire, beaucoup d’objections sont de la forme “il n’y a pas assez d’échantillons, donc ce que vous voyez pourrait être entièrement faux” – des idées vagues sur le hasard. Mais si quelque chose d’une quelconque description reconnaissable , pas seulement dans une routine ou la routine étant active, est effectif pendant 30% du temps, alors la probabilité de le voir sur un échantillon donné est de 30%.

Supposons alors que 10 échantillons seulement soient pris. Le nombre de fois que le problème sera vu dans 10 échantillons suit une dissortingbution binomiale , et la probabilité de le voir 0 fois est de 0,028. La probabilité de le voir 1 fois est de .121. Pour 2 fois, la probabilité est 0,233 et pour 3 fois elle est 0,267, après quoi elle tombe. Puisque la probabilité de le voir moins de deux fois est de 0,028 + 0,121 = 0,139, cela signifie que la probabilité de le voir deux fois ou plus est de 1 – 0,139 = 0,831. La règle générale est que si vous voyez quelque chose que vous pourriez corriger sur deux échantillons ou plus, cela vaut la peine de le réparer.

Dans ce cas, la probabilité de le voir dans 10 échantillons est de 86%. Si vous êtes dans les 14% qui ne le voient pas, prenez simplement plus d’échantillons jusqu’à ce que vous le fassiez. (Si le nombre d’échantillons est augmenté à 20, la chance de le voir deux fois ou plus augmente à plus de 99%.) Donc, il n’a pas été mesuré avec précision, mais il a été trouvé avec précision et il est important de comprendre que Cela pourrait facilement être quelque chose qu’un profileur ne pourrait pas trouver, comme quelque chose impliquant l’état des données, pas le compteur du programme.

    Sur les serveurs Java, il a toujours été utile de faire 2-3 raccourcis rapides dans une ligne et d’obtenir 2-3 threaddumps de tous les threads en cours d’exécution. Il suffit de regarder où se trouvent tous les threads pour identifier rapidement vos problèmes de performances.

    Cette technique peut révéler plus de problèmes de performance en 2 minutes que toute autre technique que je connais.

    Parce que parfois ça marche, et parfois ça donne des réponses complètement fausses. Un profileur a beaucoup plus de chances de trouver la bonne réponse, et il arrive généralement plus rapidement.

    Faire cela manuellement ne peut pas vraiment s’appeler “rapide” ou “efficace”, mais il existe plusieurs outils de profilage qui le font automatiquement; également connu sous le nom de profilage statistique .

    L’échantillonnage de la stack d’appels est une technique très utile pour le profilage, en particulier lorsque l’on considère un code complexe de grande taille qui pourrait passer son temps dans de nombreux endroits. Il a l’avantage de mesurer l’utilisation du processeur par heure d’horloge, ce qui est important pour l’interactivité, et obtenir des stacks d’appels avec chaque échantillon permet de voir pourquoi une fonction est appelée. Je l’utilise beaucoup, mais j’utilise des outils automatisés, tels que Luke Stackwalker et OProfile, ainsi que divers éléments fournis par les fournisseurs de matériel.

    La raison pour laquelle je préfère les outils automatisés à l’échantillonnage manuel pour mon travail est la puissance statistique . Prendre dix échantillons à la main, c’est bien quand vous avez une fonction occupant 40% du temps d’exécution, car vous en aurez en moyenne quatre, et toujours au moins un. Mais vous avez besoin de plus d’échantillons lorsque vous avez un profil plat, avec des centaines de fonctions feuille, aucune ne prenant plus de 1,5% du temps d’exécution.

    Disons que vous avez un lac avec de nombreux types de poissons. Si 40% des poissons du lac sont des saumons (et 60% de «tout le rest»), il suffit alors d’attraper dix poissons pour savoir qu’il y a beaucoup de saumon dans le lac. Mais si vous avez des centaines d’espèces de poissons différentes et que chaque espèce ne dépasse pas 1%, vous devrez attraper plus de dix poissons pour pouvoir dire “ce lac contient 0,8% de saumon et 0,6% de truite . ”

    De même, dans les jeux sur lesquels je travaille, il existe plusieurs systèmes majeurs, chacun appelant des dizaines de fonctions dans des centaines d’entités différentes, et tout cela se produit 60 fois par seconde. Le temps de certaines de ces fonctions se transforme en opérations courantes (comme malloc ), mais la plupart ne le font pas et, de toute façon, aucune feuille unique n’occupe plus de 1000 µs par image.

    Je peux regarder les fonctions du coffre et voir “nous passons 10% de notre temps en collision”, mais ce n’est pas très utile: j’ai besoin de savoir exactement en collision, donc je sais quelles fonctions presser. Il suffit de “faire moins de collision” pour vous en sortir, en particulier lorsque cela implique de jeter des fonctionnalités. Je préférerais savoir que “nous passons en moyenne 600 ms / frame en cache à la fin de l’octree parce que le missile magique se déplace si vite et touche beaucoup de cellules”, car je peux alors retrouver le correctif exact: soit un meilleur arbre, soit des missiles plus lents.

    L’échantillonnage manuel serait bien s’il y avait une grosse ssortingcmp 20%, disons, de ssortingcmp , mais avec nos profils, ce n’est pas le cas. Au lieu de cela, j’ai des centaines de fonctions dont j’ai besoin, par exemple, de 0,6% du cadre à 0,4% du cadre. Je dois raser 10 μs toutes les 50 μs, soit 300 fois par seconde. Pour obtenir ce genre de précision, j’ai besoin de plus d’échantillons.

    Mais au fond, ce que Luke Stackwalker fait, c’est ce que vous décrivez: chaque milliseconde ou plus, il arrête le programme et enregistre la stack d’appels (y compris les instructions précises et le numéro de ligne de l’ IP ). Certains programmes ne nécessitent que des dizaines de milliers d’échantillons pour être utilement profilés.

    (Nous en avons déjà parlé, bien sûr, mais j’ai pensé que c’était un bon endroit pour résumer le débat.)

    Il y a une différence entre les choses que les programmeurs font réellement et les choses qu’ils recommandent aux autres.

    Je connais beaucoup de programmeurs (y compris moi-même) qui utilisent cette méthode. Cela aide vraiment à trouver le plus évident des problèmes de performance, mais c’est rapide et sale et ça marche.

    Mais je ne dirais pas vraiment aux autres programmeurs de le faire, car cela me prendrait trop de temps pour expliquer toutes les réserves. Il est beaucoup trop facile de tirer des conclusions inexactes en se basant sur cette méthode, et il y a beaucoup de domaines où cela ne fonctionne pas du tout. (par exemple, cette méthode ne révèle aucun code déclenché par une entrée utilisateur).

    Donc, tout comme l’utilisation de détecteurs de mensonge au sortingbunal, ou la déclaration “goto”, nous ne recommandons pas que vous le fassiez, même s’ils ont tous leur utilité.

    Je suis surpris par le ton religieux des deux côtés.

    Le profilage est génial et est certainement plus raffiné et précis lorsque vous pouvez le faire. Parfois, vous ne pouvez pas, et c’est bien d’avoir une sauvegarde fiable. La technique de pause est comme le tournevis manuel que vous utilisez lorsque votre outil élecsortingque est trop éloigné ou que les batteries sont usées.

    Voici une courte histoire vraie. Une application (une sorte de tâche de traitement par lots) fonctionnait bien depuis six mois en production. Soudain, les opérateurs appellent les développeurs car ils vont “trop ​​lentement”. Ils ne vont pas nous laisser attacher un profileur d’échantillonnage en production! Vous devez travailler avec les outils déjà installés. Sans arrêter le processus de production, en utilisant simplement Process Explorer (que les opérateurs avaient déjà installé sur la machine), nous pourrions voir un instantané de la stack d’un thread. Vous pouvez jeter un coup d’œil sur le haut de la stack, le supprimer avec la touche Entrée et obtenir un autre instantané avec un autre clic de souris. Vous pouvez facilement obtenir un échantillon à peu près toutes les secondes.

    Il ne faut pas longtemps pour voir si la partie supérieure de la stack se trouve le plus souvent dans la DLL de la bibliothèque du client de firebase database (en attente sur la firebase database) ou dans une autre DLL système (en attente d’une opération système). application elle-même. Dans ce cas, si je me souviens bien, nous avons rapidement remarqué que 8 fois sur 10, l’application se trouvait dans un fichier DLL système appelant la lecture ou l’écriture d’un fichier réseau. Bien sûr, des “mises à niveau” récentes ont modifié les caractéristiques de performance d’un partage de fichiers. Sans une approche rapide et sale (sanctionnée par l’administrateur système) pour voir ce que l’application faisait en production , nous aurions passé beaucoup plus de temps à essayer de mesurer le problème que de corriger le problème.

    D’un autre côté, lorsque les exigences de performance vont au-delà de «suffisamment» pour vraiment repousser les limites, un profileur devient essentiel afin que vous puissiez essayer de raser les cycles de tous vos dix ou vingt premiers points névralgiques. Même si vous ne faites qu’exiger des performances modérées pendant un projet, lorsque vous pouvez disposer des outils appropriés pour vous aider à mesurer et à tester, et même les intégrer dans votre processus de test automatisé, cela peut être extrêmement utile.

    Mais quand le courant est coupé (pour ainsi dire) et que les stacks sont épuisées, il est bon de savoir comment utiliser ce tournevis manuel.

    Donc, la réponse directe: Sachez ce que vous pouvez apprendre en arrêtant le programme, mais n’ayez pas peur des outils de précision non plus. Plus important encore, sachez quels emplois font appel à quels outils.

    Si nous prenons la question “Pourquoi n’est-ce pas mieux connu?” alors la réponse va être subjective. On peut supposer que la raison pour laquelle il n’est pas mieux connu est que le profilage fournit une solution à long terme plutôt qu’une solution à un problème actuel. Il n’est pas efficace pour les applications multithread et n’est pas efficace pour les applications telles que les jeux, qui consacrent une grande partie de leur temps à la restitution.

    De plus, dans les applications à thread unique, si vous avez une méthode que vous prévoyez d’utiliser le plus souvent et que vous souhaitez réduire le temps d’exécution de toutes les autres méthodes, il sera plus difficile de déterminer les méthodes secondaires à utiliser. en premier.

    Votre processus de profilage est une méthode acceptable qui peut fonctionner et fonctionne, mais le profilage vous fournit plus d’informations et présente l’avantage de vous montrer des améliorations de performances et des régressions plus détaillées.

    Si vous avez du code bien instrumenté, vous pouvez examiner plus que la durée de la méthode; vous pouvez voir toutes les méthodes.

    Avec profilage:

    • Vous pouvez ensuite réexécuter votre scénario après chaque modification pour déterminer le degré d’amélioration des performances / de régression.

    • Vous pouvez profiler le code sur différentes configurations matérielles pour déterminer si votre matériel de production sera suffisant.

    • Vous pouvez définir le code sous des scénarios de test de charge et de charge afin de déterminer l’impact du volume d’informations sur les performances.

    • Vous pouvez aider les développeurs débutants à visualiser plus facilement l’impact de leurs modifications sur votre code, car ils peuvent redéfinir le code dans six mois lorsque vous êtes à la plage, au pub ou les deux. Beach-pub, ftw.

    Le profilage prend plus de poids car le code d’entreprise doit toujours avoir un certain degré de profilage en raison des avantages qu’il apporte à l’organisation sur une longue période. Plus le code est important, plus vous faites du profilage et des tests.

    Votre approche est valide et un autre élément est la boîte à outils du développeur. Cela ne fait que l’emporter sur le profilage.

    Le fait d’appuyer sur le bouton de pause pendant l’exécution d’un programme en mode “debug” risque de ne pas fournir les bonnes données pour effectuer des optimisations de performances. Pour parler franchement, c’est une forme de profilage grossière.

    Si vous devez éviter d’utiliser un profileur, il vaut mieux utiliser un enregistreur, puis appliquer un facteur de ralentissement à “estimation” où se trouve le véritable problème. Les profileurs sont cependant de meilleurs outils pour évaluer la situation.

    La raison pour laquelle appuyer sur le bouton pause en mode débogage peut ne pas donner une image réelle du comportement de l’application est que les débogueurs introduisent du code exécutable supplémentaire qui peut ralentir certaines parties de l’application. On peut se référer au blog de Mike Stall sur les raisons possibles du ralentissement des applications dans un environnement de débogage. Le post met en lumière certaines raisons telles que trop de points d’arrêt, création d’objects d’exception, code non optimisé, etc. La partie concernant le code non optimisé est importante – le mode “debug” entraînera de nombreuses optimisations (généralement commande) étant lancée hors de la fenêtre, pour permettre à l’hôte de débogage (le processus exécutant votre code) et à l’EDI de synchroniser l’exécution du code. Par conséquent, appuyer plusieurs fois sur pause en mode “debug” peut être une mauvaise idée.

    Les profileurs d’échantillonnage ne sont utiles que lorsque

    1. Vous surveillez un runtime avec un petit nombre de threads. De préférence un.
    2. La profondeur de la stack d’appels de chaque thread est relativement faible (pour réduire l’incroyable surcoût lié à la collecte d’un échantillon).
    3. Vous ne vous préoccupez que de l’heure de l’horloge murale et pas des autres compteurs ou des goulots d’étranglement des ressources.
    4. Vous n’avez pas instrumenté le code à des fins de gestion et de surveillance (d’où les demandes de vidage de la stack)
    5. Vous pensez à tort que la suppression d’un cadre de stack est une stratégie efficace d’amélioration des performances, que les coûts inhérents (à l’exclusion des petits porteurs) soient pratiquement nuls ou non
    6. Vous ne pouvez pas être dérangé d’apprendre comment appliquer l’ingénierie des performances logicielles au quotidien dans votre travail
    7. ….

    Les instantanés de trace de stack ne vous permettent de voir que les rayons X stroboscopiques de votre application. Vous pourriez avoir besoin de plus de connaissances accumulées par un profileur.

    L’astuce consiste à bien connaître vos outils et à choisir le meilleur pour le travail à accomplir.

    Ce sont des exemples sortingviaux avec lesquels vous travaillez pour obtenir des résultats utiles avec votre méthode. Je ne peux pas penser à un projet où le profilage était utile (par quelque méthode que ce soit) qui aurait obtenu des résultats décents avec votre méthode “rapide et efficace”. Le temps nécessaire pour démarrer et arrêter certaines applications remet en cause votre assertion “rapide”.

    Encore une fois, avec les programmes non sortingviaux, la méthode que vous préconisez est inutile.

    EDIT: En ce qui concerne “pourquoi n’est-il pas mieux connu”?

    Dans mon expérience, les revues de code évitent un code et des algorithmes de qualité médiocre, et le profilage les trouverait également. Si vous souhaitez continuer avec votre méthode, c’est formidable – mais je pense que pour la plupart des professionnels, c’est tellement loin de la liste des choses à essayer que le renforcement positif ne sera jamais un bon usage du temps.

    Il semble que ce soit assez imprécis avec les petits ensembles d’échantillons et pour obtenir de grands ensembles d’échantillons, cela prendrait beaucoup de temps, ce qui aurait été mieux dépensé avec d’autres activités utiles.

    Que se passe-t-il si le programme est en production et utilisé simultanément par des clients payants ou des collègues? Un profileur vous permet d’observer sans interférer (autant, car le principe de Heisenberg aura bien sûr un certain succès).

    Le profilage peut également vous donner des rapports précis beaucoup plus riches et plus détaillés. Ce sera plus rapide à long terme.

    EDIT 2008/11/25: OK, la réponse de Vineet m’a finalement fait voir quel est le problème ici. Mieux vaut tard que jamais.

    D’une manière ou d’une autre, l’idée de perdre du terrain est que les problèmes de performance sont détectés en mesurant les performances. C’est un moyen confus avec des fins. D’une manière ou d’une autre, j’ai évité cela en programmant des programmes entiers depuis longtemps. Je ne me suis pas battu pour le ralentir à la vitesse humaine. J’essayais de voir s’il faisait des choses fausses ou inutiles. C’est comme ça que le logiciel est rapide – trouvez et supprimez les opérations inutiles.

    Personne n’a la patience de faire des pas à pas ces jours-ci, mais la meilleure chose à faire est de choisir un nombre de cycles au hasard et de demander quelles sont leurs raisons. (C’est ce que la stack d’appels peut souvent vous dire.) Si un bon pourcentage d’entre eux n’ont pas de bonnes raisons, vous pouvez faire quelque chose.

    Il est plus difficile ces jours-ci, avec le threading et l’asynchronie, mais c’est comme ça que je règle les logiciels – en trouvant des cycles inutiles. Pas en voyant à quelle vitesse il est – je le fais à la fin.


    Voici pourquoi l’échantillonnage de la stack d’appels ne peut pas donner une mauvaise réponse, et pourquoi pas beaucoup d’échantillons sont nécessaires.

    Pendant l’intervalle d’intérêt, lorsque le programme prend plus de temps que vous ne le souhaiteriez, la stack d’appels existe continuellement, même si vous ne l’échantillonnez pas.

    • Si une instruction I est sur la stack d’appels pour la fraction P (I) de ce temps, la supprimer du programme, si possible, permettrait d’économiser exactement autant. Si cela n’est pas évident, réfléchissez un peu.

    Si l’instruction apparaît sur M = 2 échantillons ou plus, en dehors de N, son P (I) est approximativement de M / N et est certainement significatif.

    La seule façon de ne pas voir les instructions est de chronométrer magiquement tous vos échantillons lorsque l’instruction n’est pas sur la stack d’appels. Le simple fait qu’il soit présent pendant une fraction de temps est ce qui l’expose à vos sondes.

    Ainsi, le processus de réglage des performances consiste simplement à sélectionner des instructions (principalement des instructions d’appel de fonctions) qui leur permettent de se présenter en sélectionnant plusieurs échantillons de la stack d’appels. Ce sont les grands arbres de la forêt.

    Notez que nous n’avons pas à nous soucier du graphe d’appel, ni à combien de temps les fonctions prennent, ou combien de fois ils sont appelés, ou récursivité.

    Je suis contre l’obscurcissement, pas contre les profileurs. Ils vous donnent beaucoup de statistiques, mais la plupart ne donnent pas de P (I), et la plupart des utilisateurs ne réalisent pas que c’est ce qui compte.

    Vous pouvez parler des forêts et des arbres, mais pour tout problème de performances que vous pouvez résoudre en modifiant le code, vous devez modifier les instructions, en particulier les instructions avec P (I) élevé. Donc, vous devez savoir où ils sont, de préférence sans jouer Sherlock Holmes. Stack sampling tells you exactly where they are.

    This technique is harder to employ in multi-thread, event-driven, or systems in production. That’s where profilers, if they would report P(I), could really help.

    Stepping through code is great for seeing the nitty-gritty details and troubleshooting algorithms. It’s like looking at a tree really up close and following each vein of bark and branch individually.

    Profiling lets you see the big picture, and quickly identify trouble points — like taking a step backwards and looking at the whole forest and noticing the tallest trees. By sorting your function calls by length of execution time, you can quickly identify the areas that are the trouble points.

    I used this method for Commodore 64 BASIC many years ago. It is surprising how well it works.

    I’ve typically used it on real-time programs that were overrunning their timeslice. You can’t manually stop and restart code that has to run 60 times every second.

    I’ve also used it to track down the bottleneck in a comstackr I had written. You wouldn’t want to try to break such a program manually, because you really have no way of knowing if you are breaking at the spot where the bottlenck is, or just at the spot after the bottleneck when the OS is allowed back in to stop it. Also, what if the major bottleneck is something you can’t do anything about, but you’d like to get rid of all the other largeish bottlenecks in the system? How to you prioritize which bottlenecks to attack first, when you don’t have good data on where they all are, and what their relative impact each is?

    The larger your program gets, the more useful a profiler will be. If you need to optimize a program which contains thousands of conditional twigs, a profiler can be indispensible. Feed in your largest sample of test data, and when it’s done import the profiling data into Excel. Then you check your assumptions about likely hot spots against the actual data. There are always sursockets.