<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://haypo.alwaysdata.net:443/wiki/skins/common/feed.css?63"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
		<id>http://haypo.alwaysdata.net:443/wiki/index.php?action=history&amp;feed=atom&amp;title=Optimisation</id>
		<title>Optimisation - Historique des versions</title>
		<link rel="self" type="application/atom+xml" href="http://haypo.alwaysdata.net:443/wiki/index.php?action=history&amp;feed=atom&amp;title=Optimisation"/>
		<link rel="alternate" type="text/html" href="http://haypo.alwaysdata.net:443/wiki/index.php?title=Optimisation&amp;action=history"/>
		<updated>2026-04-30T20:13:06Z</updated>
		<subtitle>Historique pour cette page sur le wiki</subtitle>
		<generator>MediaWiki 1.10.1</generator>

	<entry>
		<id>http://haypo.alwaysdata.net:443/wiki/index.php?title=Optimisation&amp;diff=6665&amp;oldid=prev</id>
		<title>Haypo le 3 mars 2008 à 21:28</title>
		<link rel="alternate" type="text/html" href="http://haypo.alwaysdata.net:443/wiki/index.php?title=Optimisation&amp;diff=6665&amp;oldid=prev"/>
				<updated>2008-03-03T21:28:59Z</updated>
		
		<summary type="html">&lt;p&gt;&lt;/p&gt;

			&lt;table border='0' width='98%' cellpadding='0' cellspacing='4' style=&quot;background-color: white;&quot;&gt;
			&lt;tr&gt;
				&lt;td colspan='2' width='50%' align='center' style=&quot;background-color: white;&quot;&gt;← Version précédente&lt;/td&gt;
				&lt;td colspan='2' width='50%' align='center' style=&quot;background-color: white;&quot;&gt;Version du 3 mars 2008 à 21:28&lt;/td&gt;
			&lt;/tr&gt;
		&lt;tr&gt;&lt;td colspan=&quot;2&quot; align=&quot;left&quot;&gt;&lt;strong&gt;Ligne 1&amp;nbsp;:&lt;/strong&gt;&lt;/td&gt;
&lt;td colspan=&quot;2&quot; align=&quot;left&quot;&gt;&lt;strong&gt;Ligne 1&amp;nbsp;:&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;+&lt;/td&gt;&lt;td style=&quot;background: #cfc; font-size: smaller;&quot;&gt;&amp;lt;div style=&amp;quot;float: right; margin-bottom: 1ex; text-align: right;&amp;quot;&amp;gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;+&lt;/td&gt;&lt;td style=&quot;background: #cfc; font-size: smaller;&quot;&gt;'''Â« ''Premature optimization is the root of all evil'' Â» â€” Donald Knuth'''&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;+&lt;/td&gt;&lt;td style=&quot;background: #cfc; font-size: smaller;&quot;&gt;&amp;lt;/div&amp;gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; font-size: smaller;&quot;&gt;[[CatÃ©gorie:Programmation]] [[CatÃ©gorie:Logiciel libre]]&lt;/td&gt;&lt;td&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; font-size: smaller;&quot;&gt;[[CatÃ©gorie:Programmation]] [[CatÃ©gorie:Logiciel libre]]&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; font-size: smaller;&quot;&gt;{{Retour|Programmation|Retour aux articles de programmation}}&lt;/td&gt;&lt;td&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; font-size: smaller;&quot;&gt;{{Retour|Programmation|Retour aux articles de programmation}}&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Haypo</name></author>	</entry>

	<entry>
		<id>http://haypo.alwaysdata.net:443/wiki/index.php?title=Optimisation&amp;diff=6031&amp;oldid=prev</id>
		<title>Haypo: /* Toujours plus loin */</title>
		<link rel="alternate" type="text/html" href="http://haypo.alwaysdata.net:443/wiki/index.php?title=Optimisation&amp;diff=6031&amp;oldid=prev"/>
				<updated>2006-12-31T17:20:15Z</updated>
		
		<summary type="html">&lt;p&gt;&lt;span class=&quot;autocomment&quot;&gt;Toujours plus loin&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Nouvelle page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;[[CatÃ©gorie:Programmation]] [[CatÃ©gorie:Logiciel libre]]&lt;br /&gt;
{{Retour|Programmation|Retour aux articles de programmation}}&lt;br /&gt;
&lt;br /&gt;
L''''optimisation''' d'un programme est une tÃ¢che trÃ¨s complexe contrairemenet Ã  ce qu'on pourrait penser Ã  premiÃ¨re vue. Cet article dÃ©taille les points cruciaux Ã  observer lorsqu'on veut optimiser un programme.&lt;br /&gt;
&lt;br /&gt;
== N'optimiser que ce quand c'est vraiment nÃ©cessaire ==&lt;br /&gt;
&lt;br /&gt;
Il faut Ã©viter :&lt;br /&gt;
* d'optimiser trop tÃ´t :&lt;br /&gt;
** avant que le programme ne soit plainement fonctionnel&lt;br /&gt;
** avant d'avoir chercher plusieurs faÃ§ons d'avoir au mÃªme but mais avec des algorithmes diffÃ©rents&lt;br /&gt;
* les micro-optimisations : optimisation faisant gagner moins de 5% de performance&lt;br /&gt;
* une optimisation rendant le code obscure / illisible&lt;br /&gt;
&lt;br /&gt;
== Outil de mesure ==&lt;br /&gt;
&lt;br /&gt;
Optimiser sans outil de mesure n'a aucun sens. Il '''faut'' absoluement pouvoir mesurer le temps d'exÃ©cution (voir Ã©galement l'empreinte mÃ©moire) pour pouvoir comparer deux algorithmes. Voici quelques outils de mesure :&lt;br /&gt;
&lt;br /&gt;
* Instruction machine '''RDTSC''' des processeurs Pentium : voir l'article [[FrÃ©quence_du_processeur]], prÃ©cision : frÃ©quence du processeur (donc d'une prÃ©cision optimale)&lt;br /&gt;
* Fonction clock() de la libc, prÃ©cision : ''CLOCKS_PER_SEC'' Hertz (NdR: est-ce vraiment cette frÃ©quence lÃ ?)&lt;br /&gt;
* Fonction time.time() en Python, prÃ©cision : ??? (dÃ©pend de l'implÃ©mentation de Python)&lt;br /&gt;
&lt;br /&gt;
=== Python : classe Benchmark ===&lt;br /&gt;
&lt;br /&gt;
Pour comparer deux fonctions simples, Python offre le module timeit. Mais j'ai dÃ©veloppÃ© ma propre classe Benchmark pour rendre cette tÃ¢che encore plus simple.&lt;br /&gt;
* Voir le '''[http://hachoir.org/browser/hachoir-core/trunk/hachoir_core/benchmark.py code source de la classe Benchmark]'''&lt;br /&gt;
&lt;br /&gt;
ParamÃ¨tres de cette classe (du plus important au moins important) :&lt;br /&gt;
* Nombre d'itÃ©ration minimum (dÃ©faut: 5)&lt;br /&gt;
* Temps d'exÃ©cution maximum recommandÃ© (limite le nombre d'itÃ©rations) (dÃ©faut: 5 secondes)&lt;br /&gt;
* Nombre d'itÃ©ration maximum (paramÃ¨tre trÃ¨s peu utile en fait)&lt;br /&gt;
&lt;br /&gt;
On peut Ã©galement demander Ã  afficher la progression de la mesure (avec rafraichissement 4 fois par seconde), dÃ©sactiver le ramasse miette (''garbage collector''), etc.&lt;br /&gt;
&lt;br /&gt;
Le gros avantage de cette classe est qu'elle calcule elle-mÃªme le bon nombre d'itÃ©rations Ã  faire. Elle calcule Ã©galement le temps minimum, maximum, moyen et total.&lt;br /&gt;
&lt;br /&gt;
=== Exemple d'utilisation de Benchmark ===&lt;br /&gt;
&lt;br /&gt;
Code pour comparer deux fonctions :&lt;br /&gt;
 from benchmark import Benchmark&lt;br /&gt;
 &lt;br /&gt;
 def funcA():&lt;br /&gt;
     text = &amp;quot;&amp;quot;&lt;br /&gt;
     for index in xrange(1000):&lt;br /&gt;
         text += str(index)&lt;br /&gt;
     return text&lt;br /&gt;
 &lt;br /&gt;
 def funcB():&lt;br /&gt;
     text = []&lt;br /&gt;
     for index in xrange(1000):&lt;br /&gt;
         text += str(index)&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;return ''.join(text)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 b = Benchmark(max_time=1.0)&lt;br /&gt;
 b.run(funcA)&lt;br /&gt;
 b.run(funcB)&lt;br /&gt;
&lt;br /&gt;
RÃ©sultat :&lt;br /&gt;
 Run benchmark: 935 calls (estimate: 999.58 ms)&lt;br /&gt;
 Benchmark: '''best=967.03 usec'''  average=1.00 ms  worst=10.63 ms  total=940.08 ms&lt;br /&gt;
 Run benchmark: 613 calls (estimate: 998.50 ms)&lt;br /&gt;
 Benchmark: '''best=1.49 ms'''  average=1.57 ms  worst=11.02 ms  total=960.96 ms&lt;br /&gt;
&lt;br /&gt;
967 Âµsec contre 1490 Âµsec, la fonction ''funcA()'' est donc plus rapide.&lt;br /&gt;
&lt;br /&gt;
== Trouver la partie Ã  optimiser ==&lt;br /&gt;
&lt;br /&gt;
Une tÃ¢che trÃ¨s importante et qui amÃ¨ne sur de fausses pistes si elle est faite Ã  la va-vite : trouver la partie prÃ©cise Ã  optimiser. En gÃ©nÃ©ral, un programme passe 90% de son temps dans 10% du code. Il faut optimiser ces 10% et surtout ne pas perdre de temps sur les autres 90% qui sont rarement utilisÃ©s.&lt;br /&gt;
&lt;br /&gt;
De toute maniÃ¨re, quand on optimise une partie peu gourmande en ressource, on &amp;quot;voit&amp;quot; facilement avec un outil de mesure que Ã§a ne change rien (moins de 5% de gain) aux performances.&lt;br /&gt;
&lt;br /&gt;
Trouver la portion de code qui consomme le maximum de ressource processeur peut Ãªtre faite facilement avec un ''profiler''. Voici un petite liste (loin d'Ãªtre exhaustive) :&lt;br /&gt;
&lt;br /&gt;
* [http://oprofile.sourceforge.net/ oprofile] : ne nÃ©cessite pas de recompilation&lt;br /&gt;
* [http://www.gnu.org/software/binutils/ gprof] : nÃ©cessite une recompilation&lt;br /&gt;
* [http://docs.python.org/lib/profile.html Modules python profiler et hotshot] : simple Ã  mettre en Å“uvre&lt;br /&gt;
&lt;br /&gt;
== Cas pratique : optimisation d'urwid en UTF-8 ==&lt;br /&gt;
&lt;br /&gt;
=== Trouver le code Ã  optimiser ===&lt;br /&gt;
&lt;br /&gt;
urwid est une bibliothÃ¨que permettant de concevoir facilement des interfaces en mode texte. La version 0.9.7 souffre de lenteur lorqu'on l'utilise dans un terminal avec le charset UTF-8. LanÃ§ons un profileur (modules Python hotshot et stat) pour voir quel est le code Ã  optimiser :&lt;br /&gt;
         2099491 function calls (2099179 primitive calls) in '''8.134 CPU seconds'''&lt;br /&gt;
 &lt;br /&gt;
   Ordered by: internal time, call count&lt;br /&gt;
   List reduced from 247 to 50 due to restriction &amp;lt;50&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)&lt;br /&gt;
     '''9685    3.546    0.000    6.717    0.001 util.py:557(calc_width)'''&lt;br /&gt;
   '''982880    1.606    0.000    1.606    0.000 utable.py:79(decode_one)'''&lt;br /&gt;
   '''983865    1.598    0.000    1.598    0.000 utable.py:69(get_width)'''&lt;br /&gt;
     1786    0.243    0.000    0.243    0.000 canvas.py:366(arange)&lt;br /&gt;
       76    0.214    0.003    0.334    0.004 curses_display.py:484(draw_screen)&lt;br /&gt;
     2090    0.133    0.000    6.900    0.003 canvas.py:37(__init__)&lt;br /&gt;
     1786    0.086    0.000    0.952    0.001 canvas.py:354(apply_text_layout)&lt;br /&gt;
    21090    0.048    0.000    0.048    0.000 util.py:869(rle_len)&lt;br /&gt;
     3760    0.046    0.000    0.193    0.000 urwid_ui.py:252(_get)&lt;br /&gt;
     (...)&lt;br /&gt;
&lt;br /&gt;
Informations importantes :&lt;br /&gt;
* Temps total d'exÃ©cution : 8.1 secondes&lt;br /&gt;
* Fonctions consommant le plus de temps processeur : calc_width(), decode_one(), get_width()&lt;br /&gt;
&lt;br /&gt;
Il faut donc se concentrer sur ces 3 fonctions :&lt;br /&gt;
* decode_one() extrait le premire caractÃ¨re Unicode d'une chaÃ®ne UTF-8&lt;br /&gt;
* get_width() calcule la largeur Ã  l'Ã©cran d'un caractÃ¨re Unicode&lt;br /&gt;
* calc_width() calcule la largeur Ã  l'Ã©cran d'une chaÃ®ne de caractÃ¨re et utilise decode_one() et get_width()&lt;br /&gt;
&lt;br /&gt;
=== Fausse piste ===&lt;br /&gt;
&lt;br /&gt;
Au dÃ©but, j'Ã©tais parti sur une mauvaise piste : chercher Ã  optimiser decode_one() ou get_width(), travail long, laborieux et surtout qui n'avait aucun impact sur les performances (gain entre 1 et 2%).&lt;br /&gt;
&lt;br /&gt;
=== Unicode or not Unicode: that is the question ===&lt;br /&gt;
&lt;br /&gt;
En lisant le code plus attentivement, j'ai compris qu'urwid acceptait soit des chaÃ®nes encodÃ©es soit en Unicode soit dans un autre charset (ex: UTF-8). En bref, le classe Canvas n'acceptait que des chÃ¢ines non-Unicode en entrÃ©e: il fallait convertir de l'Unicode en UTF-8 si besoin. Alors qu'elle reconvertissait en Unicode quelques instants plus tard... Mais surtout d'une maniÃ¨re complexe et coÃ»teuse.&lt;br /&gt;
&lt;br /&gt;
J'ai alors fait en sorte qu'urwid conserve les chaÃ®nes en Unicode le plus longtemps possible : j'ai stockÃ© les chaÃ®nes deux fois dans Canvas : encodÃ©es en UTF-8 et en Unicode.&lt;br /&gt;
&lt;br /&gt;
Les rÃ©sultats sont trÃ¨s encourageants :&lt;br /&gt;
* Urwid normal : 7.8 sec&lt;br /&gt;
* En conservant les chaÃ®nes Unicode : 4.1 sec&lt;br /&gt;
&lt;br /&gt;
Le programme est donc deux fois plus rapide (100% plus rapide). Ceci montre qu'on a touchÃ© la corde sensible d'urwid.&lt;br /&gt;
&lt;br /&gt;
=== Toujours plus loin ===&lt;br /&gt;
&lt;br /&gt;
En discutant avec Julien, il m'a suggÃ©rÃ© de cacher widths : tableau contenant le rÃ©sultat des appels Ã  calc_width() pour chaque Canvas. Effectivement, j'avais stockÃ©s les chaÃ®nes Unicode pour accÃ©lÃ©rer le calcul de widths mais en fait il suffisait de stocker widths. Ce qui Ã©vitait en plus de recalculer widths plus tard.&lt;br /&gt;
&lt;br /&gt;
Nouveau rÃ©sultats :&lt;br /&gt;
* Urwid normal : 7.8 sec&lt;br /&gt;
* En stockant widths : 2.7 sec&lt;br /&gt;
&lt;br /&gt;
=== Tentons autre chose ===&lt;br /&gt;
&lt;br /&gt;
En prenant du recul, j'ai rÃ©alisÃ© que tout tournait autour de la fonction calc_width()... ce qui est d'ailleurs clair en regardant les rÃ©sultats du ''profiler''. Quand on n'arrive plus Ã  amÃ©liorer l'algorithme d'une fonction, une bonne idÃ©e est de prÃ©calculer ou cacher les rÃ©sultats.&lt;br /&gt;
&lt;br /&gt;
Fonction Â« naÃ¯veÂ Â» pour cacher les rÃ©sultats :&lt;br /&gt;
 def calc_width( text, start_offs, end_offs ):&lt;br /&gt;
     key = hash( (text, start_offs) )&lt;br /&gt;
     if key not in calc_width.cache:&lt;br /&gt;
 	'''calc_width.cache[key] = _calc_width(text, start_offs, end_offs)'''&lt;br /&gt;
     return calc_width.cache[key]&lt;br /&gt;
 calc_width.cache = {}&lt;br /&gt;
(il faut renommer la vraie fonction calc_width() en _calc_width())&lt;br /&gt;
&lt;br /&gt;
En utilisant juste cette fonction, les rÃ©sultats sont impressionants :&lt;br /&gt;
* Urwid normal : 7,8 sec&lt;br /&gt;
* Avec le cache : 2,1 sec&lt;br /&gt;
&lt;br /&gt;
Nous sommes donc passÃ©s de 7,8 secondes Ã  4,1 secondes, puis 2,7 et finalement 2,1. Le plus drÃ´le Ã©tant que finalement, cette derniÃ¨re fonction est la plus courte (changement minimum d'urwid).&lt;/div&gt;</summary>
		<author><name>Haypo</name></author>	</entry>

	</feed>