title:      Benchmarks map, filter vs. list-comprehensions
slug:       benchmarks-map-filter-vs-list-comprehensions
date:       2006-10-25 19:00:06
type:       post
vignette:   images/logos/seb_sauvage.png
contextual_title1:  Bonnes pratiques et astuces Python
contextual_url1:    20080511-bonnes-pratiques-et-astuces-python
contextual_title2:  Python : lisibilité vs simplicité
contextual_url2:    20060425-python-et-underscore
contextual_title3:  Principales nouveautés dans Python 2.5
contextual_url3:    20060304-principales-nouveautes-dans-python-25

<p>Je viens de tomber sur les <a href="http://sebsauvage.net/python/snyppets/">snyppets de Seb Sauvage</a> (site que j'apprécie beaucoup par ailleurs) et il y a une phrase qui m'a interpellé sur le <a href="http://sebsauvage.net/python/snyppets/#zip_filter_map">paragraphe consacré  à zip, map, filter et aux list-comprehensions</a>&nbsp;:</p>


<blockquote><p>Except that {map|filter} is <strong>faster</strong>. (than list-comprehensions)</p></blockquote>


<p>Ni une, ni deux, je récupère l'<a href="http://programmation-python.org/sections/blog/2006_10_05_article-sur-code">article de Tarek</a> qui est très bon et qui comporte une fonction testant la durée d'execution des fonctions pour pouvoir comparer. J'avais <a href="https://larlet.fr/david/biologeek/archives/20060121-bonnes-pratiques-de-la-programmation-en-python/">déjà essayé d'autres fonctions</a> mais autant innover un peu.</p>

<p>Allons-y donc avec le décorateur suivant&nbsp;:</p>


<pre>from test import pystone
import time

kPs = 1000
TOLERANCE = 0.5 * kPs

def mesure_pystone():
    pystone.pystones(loops=pystone.LOOPS)
    pystone.pystones(loops=pystone.LOOPS)
    return pystone.pystones(loops=pystone.LOOPS)

def timedtest(function, local_pystones=mesure_pystone()):
    """ Decorator to measure execution time in pystones """
    def wrapper(*args, **kw):
        all = []
        for i in range(3):
            start_time = time.time()
            try:
                res = function(*args, **kw)
            finally:
                total_time = time.time() - start_time
                if total_time == 0:
                    temps = 0
                else:
                    ratio = local_pystones[0] / local_pystones[1]
                    temps = total_time / ratio
                all.append(temps)
        print '%d pystones' % min(all)
        return res
    return wrapper</pre>

<p>J'ai testé les fonctions suivantes qui ne sont pas les mêmes que les exemples de Seb mais qui retournent (elles, <a href="http://www.haypocalc.com/blog/">merci Haypo</a> :-)) le même résultat&nbsp;:</p>


<pre>@timedtest
def map_without_list_comprehension():
    for i in range(10000):
        map(abs, [-5,7,-12])

@timedtest
def map_with_list_comprehension():
    for i in range(10000):
        [abs(i) for i in [-5,7,-12]]

print '=== map vs. list-comprehension ==='
print 'map without list-comprehension:'
map_without_list_comprehension()
print 'map with list-comprehension:'
map_with_list_comprehension()

@timedtest
def filter_without_list_comprehension():
    for i in range(10000):
        filter(abs, [-5,7,0,-12] )

@timedtest
def filter_with_list_comprehension():
    for i in range(10000):
        [i for i in [-5,7,0,-12] if i]

print '=== filter vs. list-comprehension ==='
print 'filter without list-comprehension:'
filter_without_list_comprehension()
print 'filter with list-comprehension:'
filter_with_list_comprehension()</pre>

<p>Et voila les résultats obtenus&nbsp;:</p>


<blockquote><p>=== map vs. list-comprehension ===<br />
map without list-comprehension:<br />
638 pystones<br />
map with list-comprehension:<br />
534 pystones<br />
=== filter vs. list-comprehension ===<br />
filter without list-comprehension:<br />
612 pystones<br />
filter with list-comprehension:<br />
550 pystones<br /></p></blockquote>


<p>Il me semble que c'est sans appel&nbsp;: les list-comprehensions sont <strong>toujours</strong> plus rapides que map ou filter avec Python 2.4. Il faudrait tester tout ça avec Python 2.5 mais je pense que les résultats seraient encore plus significatifs.</p>
