{"id":80,"date":"2017-04-03T14:46:10","date_gmt":"2017-04-03T14:46:10","guid":{"rendered":"http:\/\/learningcenter.paratools.com\/?p=80"},"modified":"2022-04-14T14:04:48","modified_gmt":"2022-04-14T14:04:48","slug":"profilage-avec-callgrind","status":"publish","type":"post","link":"https:\/\/learningcenter.paratools.com\/?p=80","title":{"rendered":"Profilage avec Callgrind"},"content":{"rendered":"<p>Callgrind est un outil de la famille de valgrind. Il permet de faire du profilage \u00e0 grain assez fin. Cet outil est tr\u00e8s portable et permet en combinaison avec l&rsquo;outil Kcachegrind de visualiser de mani\u00e8re assez pr\u00e9cise des profils de performance.<\/p>\n<p>L&rsquo;inconv\u00e9nient principal de cet outil est son surco\u00fbt relativement important, il faut donc veiller \u00e0 cibler un cas test de taille limit\u00e9e et non un cas repr\u00e9sentatif. Apr\u00e9s avoir expos\u00e9 le processus d&rsquo;installation, nous allons \u00a0pr\u00e9senter les processus de mesure puis comment les r\u00e9sultats peuvent \u00eatre analys\u00e9s.<\/p>\n<p><!--more--><\/p>\n<h1>Installation de Callgrind et Kcachegrind<\/h1>\n<p>Dans ce tutoriel nous allons utiliser deux outils, valgrind qui fournit Callgrind et Kcachegrind qui est une interface graphique permettant de visualiser les fichiers de sortie g\u00e9n\u00e9r\u00e9s par Callgrind.<\/p>\n<p>Sur Centos 7:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nyum install kdesdk-kcachegrind valgrind graphviz\r\n<\/pre>\n<p>Sur Ubuntu:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\napt-get install kcachegrind valgrind graphviz\r\n<\/pre>\n<p>V\u00e9rifiez ensuite que les commandes \u00ab\u00a0valgrind\u00a0\u00bb et kcachegrind sont bien pr\u00e9sentes.<\/p>\n<h1>Instrumenter avec Callgrind<\/h1>\n<p>Le processus d&rsquo;instrumentation de callgrind est tr\u00e8s simple. Il suffit de rajouter l&rsquo;invocation de Callgrind avant le programme cible. Par exemple:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nvalgrind --tool=callgrind .\/test\r\n<\/pre>\n<p>Prenons maintenant un code simple pour tester le profilage (test.c):<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n#include &lt;stdio.h&gt;\r\n\r\nint main(int argc, char **argv)\r\n{\r\n   int i;\r\n   int j;\r\n\r\n   double result = 0.0;\r\n\r\n   for (i = 0; i &lt; 100000; ++i)\r\n   {\r\n      for (j = 0; j &lt; 10000; ++j) { result += i + j * 0,1; } } printf(&quot;--&gt; %g\\n&quot;, result );\r\n\r\n   return 0;\r\n}\r\n<\/pre>\n<p>Compilons le code en mode d\u00e9bug :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ngcc -g -O3 -o test .\/test.c\r\n<\/pre>\n<p>Puis instrumentons le avec Callgrind, notez que l&rsquo;ex\u00e9cution est ralentie.<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nvalgrind --tool=callgrind .\/test\r\n<\/pre>\n<p>Remarquez qu&rsquo;un fichier <b>callgrind.out.PID<\/b> a maintenant \u00e9t\u00e9 g\u00e9n\u00e9r\u00e9 dans le r\u00e9pertoire courant. Ce fichier contient les informations mesur\u00e9es lors de l&rsquo;\u00e9x\u00e9cution.<\/p>\n<h1>Visualisation en Console<\/h1>\n<p>Il est possible de consulter le contenu du profil en console en utilisant <b>callgrind_anotate<\/b>. Cet outil s&rsquo;utilise comme suit:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ncallgrind_annotate .\/callgrind.out.31458\r\n<\/pre>\n<p>Cela produira une sortie assez compacte:<\/p>\n<blockquote><p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\nProfile data file &lsquo;.\/callgrind.out.31458&rsquo; (creator: callgrind-3.11.0)<br \/>\n&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\nI1 cache:<br \/>\nD1 cache:<br \/>\nLL cache:<br \/>\nTimerange: Basic block 0 &#8211; 1000321106<br \/>\nTrigger: Program termination<br \/>\nProfiled target: .\/a.out (PID 31458, part 1)<br \/>\nEvents recorded: Ir<br \/>\nEvents shown: Ir<br \/>\nEvent sort order: Ir<br \/>\nThresholds: 99<br \/>\nInclude dirs:<br \/>\nUser annotated:<br \/>\nAuto-annotation: off<\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\nIr<br \/>\n&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\n7,000,800,236 PROGRAM TOTALS<\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\nIr file:function<br \/>\n&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\n7,000,700,025 .\/test.c:main [\/home\/jbbesnard\/a.out]<\/p><\/blockquote>\n<p>On y trouve peu d&rsquo;information pour l&rsquo;instant, uniquement dans la partie basse le fait que tous les \u00e9chantillons \u00e9taient dans la fonction main. Pour obtenir des informations plus int\u00e9ressantes, il faut passer des param\u00e8tre au programme.<\/p>\n<p>Par exemple pour visualiser les fonction appelantes et les fonction appel\u00e9es:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ncallgrind_annotate --tree=both .\/callgrind.out.31458\r\n<\/pre>\n<p>On obtient les fonction appel\u00e9es dans le main (ici printf):<\/p>\n<blockquote><p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\nIr file:function<br \/>\n&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<\/p>\n<p>7,000,700,025 * .\/test.c:main [\/home\/jbbesnard\/a.out]<br \/>\n3,900 &gt; ???:printf (1x) [\/usr\/lib64\/libc-2.17.so]<br \/>\n776 &gt; ???:_dl_runtime_resolve (1x) [\/usr\/lib64\/ld-2.17.so]<\/p><\/blockquote>\n<p>Mais on peut aller plus loin en ayant un code annot\u00e9 pour les principales fonction :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ncallgrind_annotate --auto=yes .\/callgrind.out.31458\r\n<\/pre>\n<p>On obtient en compl\u00e9ment de la sortie pr\u00e9c\u00e9dente:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n--------------------------------------------------------------------------------\r\n-- Auto-annotated source: .\/test.c\r\n--------------------------------------------------------------------------------\r\nIr\r\n\r\n.\r\n.  int main(int argc, char **argv)\r\n5  {\r\n.     int i;\r\n.     int j;\r\n.\r\n2     double result = 0.0;\r\n.\r\n300,004       for (i = 0; i &lt; 100000; ++i)\r\n.             {\r\n7,000,400,011    for (j = 0; j &lt; 10000; ++j) { result += i + j * 0,1; } } printf(&quot;--&gt; %g\\n&quot;, result );\r\n3,900         =&gt; ???:printf (1x)\r\n776           =&gt; ???:_dl_runtime_resolve (1x)\r\n.\r\n1     return 0;\r\n2  }\r\n\r\n--------------------------------------------------------------------------------\r\n Ir\r\n--------------------------------------------------------------------------------\r\n100  percentage of events annotated\r\n<\/pre>\n<p>On voit ici que la majorit\u00e9 des \u00e9chantillons sont dans leur grande majorit\u00e9 regroupp\u00e9s dans la boucle interne faisant le calcul. Cela permet de pointer pr\u00e9cis\u00e9ment le point chaud du code.<\/p>\n<h1>Controller Callgrind<\/h1>\n<p>Calgrind fournit \u00e9galement un outil callgrind_control pour interragir avec un programme en cours de profilage. Pour illustre ce point, nous allons volontairement cr\u00e9er un situation de bloquage dans le programme cible. Nous proposons le code suivant:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n#include &lt;stdio.h&gt;\r\n\r\nvoid bar()\r\n{\r\n   while(1)\r\n   {\r\n      \/* BLOCAGE *\/\r\n      sleep(1);\r\n   }\r\n}\r\n\r\nint main(int argc, char **argv)\r\n{\r\n   bar();\r\n   return 0;\r\n}\r\n<\/pre>\n<p>Compilons le code en mode d\u00e9bug :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ngcc -g -O3 -o ddlk .\/d.c\r\n<\/pre>\n<p>Et lan\u00e7ons le dans Callgrind:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nvalgrind --tool=callgrind .\/ddlk\r\n<\/pre>\n<p>Ce programme ne se terminera jamais, par contre il est possible de le surveiller \u00e0 \u00ab\u00a0distance\u00a0\u00bb avec l&rsquo;outil callgrind_control. Ce outils est tr\u00e8s utiles pour diff\u00e9rentes manipulation. Pour ce faire, nous allons laisser le programme bloqu\u00e9 s&rsquo;\u00e9x\u00e9cuter et ouvrir une autre console.<\/p>\n<p>On peut tout d&rsquo;abord observer l&rsquo;\u00e9tat du programme:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ncallgrind_control -s\r\n<\/pre>\n<blockquote><p>PID 31946: .\/ddlk<br \/>\nsending command status internal to pid 31946<br \/>\nNumber of running threads: 1, thread IDs: 1<br \/>\nEvents collected: Ir<br \/>\nFunctions: 129 (executed 2,337, contexts 129)<br \/>\nBasic blocks: 1,435 (executed 22,738, call sites 198)<\/p><\/blockquote>\n<p>On peut ensuite demander la g\u00e9n\u00e9ration d&rsquo;un backtrace \u00e0 distance:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ncallgrind_control -s\r\n<\/pre>\n<blockquote><p>PID 31946: .\/ddlk<br \/>\nsending command status internal to pid 31946<\/p>\n<p>Frame: Backtrace for Thread 1<br \/>\n[ 0] __nanosleep_nocancel (148 x)<br \/>\n[ 1] nanosleep (148 x)<br \/>\n[ 2] sleep (147 x)<br \/>\n[ 3] bar (1 x)<br \/>\n[ 4] main (1 x)<br \/>\n[ 5] (below main) (1 x)<br \/>\n[ 6] 0x000000000040044b (1 x)<br \/>\n[ 7] 0x00000000000011e0<\/p><\/blockquote>\n<p>Ici on voit clairement que le code est bloqu\u00e9 dans bar. Ceci peut \u00eatre tr\u00e8s pratique pour comprendre pourquoi un code bloque. Il est \u00e9galement possible de forcer l&rsquo;\u00e9criture des \u00e9v\u00e8nement et de d\u00e9sactiver\/r\u00e9activer l&rsquo;instrumentation (voir aide -h).<\/p>\n<h1>Visualiser avec Kcachegrind<\/h1>\n<p>Il est \u00e9galement possible d&rsquo;utliser une interface graphique pour observer un profil Valgrind. Pour ce faire, on utilise la commande suivante:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nkcachegrind .\/callgrind.out.31458\r\n<\/pre>\n<p>Vous obtiendrez alors une sortie telle que celle-ci:<\/p>\n<p><a href=\"http:\/\/learningcenter.paratools.com\/wp-content\/uploads\/2017\/04\/cg.jpeg\"><img loading=\"lazy\" class=\"aligncenter wp-image-167 size-medium\" src=\"http:\/\/learningcenter.paratools.com\/wp-content\/uploads\/2017\/04\/cg-300x171.jpeg\" alt=\"\" width=\"300\" height=\"171\" srcset=\"https:\/\/learningcenter.paratools.com\/wp-content\/uploads\/2017\/04\/cg-300x171.jpeg 300w, https:\/\/learningcenter.paratools.com\/wp-content\/uploads\/2017\/04\/cg-768x438.jpeg 768w, https:\/\/learningcenter.paratools.com\/wp-content\/uploads\/2017\/04\/cg-940x537.jpeg 940w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>Vous pouvez consulter un profil de plus grande taille en t\u00e9l\u00e9chargeant ce fichier <a href=\"http:\/\/france.paratools.com\/callgrind.out\">callgrind.out<\/a>\u00a0g\u00e9n\u00e9r\u00e9 sur l&rsquo;application Lulesh.<\/p>\n<h1>Conclusion<\/h1>\n<p>Ce tutoriel a rapidement pr\u00e9sent\u00e9 l&rsquo;utilisation de Callgrind qui bien que ralentissant l&rsquo;\u00e9x\u00e9cutio n du code est tout \u00e0 fait capable de fournir des informations d\u00e9taill\u00e9es sur son comportement. Coupl\u00e9 \u00e0 Kcachegrind, c&rsquo;est un outils indispensable. De plus valgrind fait bien plus que du profilage &#8230;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Callgrind est un outil de la famille de valgrind. Il permet de faire du profilage \u00e0 grain assez fin. Cet outil est tr\u00e8s portable et permet en combinaison avec l&rsquo;outil Kcachegrind de visualiser de mani\u00e8re assez pr\u00e9cise des profils de performance. L&rsquo;inconv\u00e9nient principal de cet outil est son surco\u00fbt relativement important, il faut donc veiller [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[4],"tags":[10,5,7,9],"_links":{"self":[{"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=\/wp\/v2\/posts\/80"}],"collection":[{"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=80"}],"version-history":[{"count":20,"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=\/wp\/v2\/posts\/80\/revisions"}],"predecessor-version":[{"id":170,"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=\/wp\/v2\/posts\/80\/revisions\/170"}],"wp:attachment":[{"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=80"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=80"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=80"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}