Question

Je tentais de trouver le meilleur moyen de compter le nombre d'éléments dans une liste correspondant à un filtre spécifique. Dans ce cas, combien trouvent de nombres impairs, il y a dans une liste.

Tout en faisant cela, j'ai été surpris par les résultats de la comparaison d'une compréhension de liste vs l'expression de générateur équivalent:

python -m timeit -s "L = xrange(1000000)" "sum([1 for i in L if i & 1])"
10 loops, best of 3: 109 msec per loop

python -m timeit -s "L = xrange(1000000)" "sum(1 for i in L if i & 1)"
10 loops, best of 3: 125 msec per loop

J'ai aussi essayé avec L une liste régulière, et de tailles différentes, mais dans tous les cas, la compréhension de la liste gagne.

Que fait de Genexp qu'il amène à être plus lent par rapport à la listcomp qui crée une nouvelle liste avec 1 million d'articles ...?

(BTW, le moyen le plus rapide que j'ai trouvé. x = 1; len(filter(x.__and__, L)) Et oui, je sais écrire du code comme ça tue les chatons, je le fais pour le plaisir)

Était-ce utile?

La solution

Lorsque la mémoire pratiquement illimitée est disponible (qui sera toujours le cas dans de minuscules points de référence, bien que souvent en problèmes réels -), les listes ont tendance à surperformer les générateurs car ils peuvent obtenir attribués qu'une seule fois, dans un « grand tas »(pas de fragmentation de la mémoire, etc.), tandis que les générateurs ont besoin (en interne) un effort supplémentaire pour éviter que « l'approche gros tas » en préservant l'état pile cadre pour permettre la reprise de l'exécution.

Si une liste-approche ou générateur approche sera plus rapide dans un véritable programme dépend de la situation exacte de la mémoire, y compris la fragmentation, ce qui est presque impossible de reproduire avec précision dans un « micro-référence » . OIEau, à la fin, si vous se soucient vraiment de la performance, vous devez soigneusement référence (et, séparément, profil) votre programme actuel (s), non seulement des micro-benchmarks « jouets », dans le cas général.

Autres conseils

D'après ce que je me souviens, un cadre de générateur doivent être activés pour chaque résultat, alors que la compréhension de liste utilise une trame d'activation. Le coût différentiel dans la compression de la liste est le coût supplémentaire de la mémoire - références int dans votre cas. La relation peut bien inverse si chaque élément est une nouvelle instance et utilise plus de mémoire.

Mise à jour: Après le test, il n'inverse

~% python -m timeit -s "L = xrange(1000000);oint=type('intEx', (int,),{})" "sum([oint(1) for i in L if i & 1])" 
10 loops, best of 3: 414 msec per loop

~% python -m timeit -s "L = xrange(1000000);oint=type('intEx', (int,),{})" "sum(oint(1) for i in L if i & 1)" 
10 loops, best of 3: 392 msec per loop
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top