web-dev-qa-db-fra.com

python: comparaison des performances deque vs list

Dans python docs, je peux voir que deque est une collection spéciale hautement optimisée pour faire apparaître/ajouter des éléments du côté gauche ou droit. Par exemple, la documentation dit:

Les Deques sont une généralisation des piles et des files d'attente (le nom est prononcé "deck" et est l'abréviation de "file d'attente à double extrémité"). Deques prend en charge les ajouts et les sauts de mémoire de thread-safe, efficaces de chaque côté du deque avec approximativement les mêmes performances O(1) dans les deux sens).

Bien que les objets de liste prennent en charge des opérations similaires, ils sont optimisés pour des opérations rapides de longueur fixe et entraînent O(n) coûts de déplacement de la mémoire pour les opérations pop (0) et insert (0, v) qui modifient les deux la taille et la position de la représentation sous-jacente des données.

J'ai décidé de faire des comparaisons en utilisant ipython. Quelqu'un pourrait-il m'expliquer ce que j'ai fait de mal ici:

In [31]: %timeit range(1, 10000).pop(0)
 10000 loops, best of 3: 114 us per loop

In [32]: %timeit deque(xrange(1, 10000)).pop() 
10000 loops, best of 3: 181 us per loop

In [33]: %timeit deque(range(1, 10000)).pop()
1000 loops, best of 3: 243 us per loop
33
Oleg Tarasenko

Could anyone explain me what I did wrong here

Oui, votre timing est dominé par le temps de créer la liste ou deque. Le temps pour faire le pop est insignifiant en comparaison.

Au lieu de cela, vous devez isoler la chose que vous essayez de tester (la vitesse pop) du temps de configuration:

In [1]: from collections import deque

In [2]: s = range(1000)

In [3]: d = deque(s)

In [4]: s_append, s_pop = s.append, s.pop

In [5]: d_append, d_pop = d.append, d.pop

In [6]: %timeit s_pop(); s_append(None)
10000000 loops, best of 3: 115 ns per loop

In [7]: %timeit d_pop(); d_append(None)
10000000 loops, best of 3: 70.5 ns per loop

Cela dit, les vraies différences entre deques et list en termes de performances sont:

  • Deques a O(1) vitesse pour appendleft () et popleft () tandis que les listes ont O(n) performances pour insérer (0, valeur) et pop (0).

  • Les performances d'ajout de liste sont aléatoires car elles utilisent realloc () sous le capot. En conséquence, il a tendance à avoir des timings trop optimistes dans du code simple (car le realloc n'a pas à déplacer les données) et des timings vraiment lents dans le code réel (car la fragmentation force realloc à déplacer toutes les données). En revanche, les performances de deque append sont cohérentes car elles ne réallouent jamais et ne déplacent jamais les données.

70

Pour ce que cela vaut:

> python -mtimeit -s 'import collections' -s 'c = collections.deque(xrange(1, 100000000))' 'c.pop()'
10000000 loops, best of 3: 0.11 usec per loop

> python -mtimeit -s 'c = range(1, 100000000)' 'c.pop()'
10000000 loops, best of 3: 0.174 usec per loop

> python -mtimeit -s 'import collections' -s 'c = collections.deque()' 'c.appendleft(1)'
10000000 loops, best of 3: 0.116 usec per loop

> python -mtimeit -s 'c = []' 'c.insert(0, 1)'
100000 loops, best of 3: 36.4 usec per loop

Comme vous pouvez le voir, où il brille vraiment est dans appendleft vs insert.

20
sberry