48 votes

Pourquoi est-startswith plus lent que le tranchage

Pourquoi la mise en œuvre de l' startwith plus lent que le tranchage?

In [1]: x = 'foobar'

In [2]: y = 'foo'

In [3]: %timeit x.startswith(y)
1000000 loops, best of 3: 321 ns per loop

In [4]: %timeit x[:3] == y
10000000 loops, best of 3: 164 ns per loop

Étonnamment, même y compris le calcul de la longueur, de découpage apparaît encore nettement plus rapide:

In [5]: %timeit x[:len(y)] == y
1000000 loops, best of 3: 251 ns per loop

Remarque: la première partie de ce comportement est noté en Python pour l'Analyse des Données (Chapitre 3), mais pas d'explication est offert.

.

Si utile: voici le code en C pour startswith; et voici la sortie de dis.dis:

In [6]: import dis

In [7]: dis_it = lambda x: dis.dis(compile(x, '<none>', 'eval'))

In [8]: dis_it('x[:3]==y')
  1           0 LOAD_NAME                0 (x)
              3 LOAD_CONST               0 (3)
              6 SLICE+2             
              7 LOAD_NAME                1 (y)
             10 COMPARE_OP               2 (==)
             13 RETURN_VALUE        

In [9]: dis_it('x.startswith(y)')
  1           0 LOAD_NAME                0 (x)
              3 LOAD_ATTR                1 (startswith)
              6 LOAD_NAME                2 (y)
              9 CALL_FUNCTION            1
             12 RETURN_VALUE 

37voto

senderle Points 41607

Certains de la différence de performance peut être expliqué par la prise en compte du temps que prend la . de l'opérateur de faire sa chose:

>>> x = 'foobar'
>>> y = 'foo'
>>> sw = x.startswith
>>> %timeit x.startswith(y)
1000000 loops, best of 3: 316 ns per loop
>>> %timeit sw(y)
1000000 loops, best of 3: 267 ns per loop
>>> %timeit x[:3] == y
10000000 loops, best of 3: 151 ns per loop

Une autre partie de la différence peut être expliquée par le fait que l' startswith est une fonction, et même les non-op appels de la fonction de prendre un peu de temps:

>>> def f():
...     pass
... 
>>> %timeit f()
10000000 loops, best of 3: 105 ns per loop

Ce n'est pas totalement expliquer la différence, depuis la version à l'aide de tranchage et d' len appelle une fonction et est encore plus rapide (à comparer avec le sw(y) ci-dessus: 267 n.-é.):

>>> %timeit x[:len(y)] == y
1000000 loops, best of 3: 213 ns per loop

Mon seul deviner ici, c'est que peut-être que Python optimise la recherche du temps pour les fonctions intégrées, ou qu' len des appels sont optimisés (qui est probablement vrai). Il pourrait être possible de tester avec une coutume len func. Ou peut-être c'est là que les différences identifiées par LastCoder coup de pied dans. Notez également larsmansrésultats, qui montrent que l' startswith est en fait plus rapide pour plus de chaînes. L'ensemble de la ligne de raisonnement ci-dessus s'applique uniquement aux cas où la surcharge je parle en fait des questions.

28voto

larsmans Points 167484

La comparaison n'est pas juste puisque vous êtes seulement en mesure de le cas où l' startswith retours True.

>>> x = 'foobar'
>>> y = 'fool'
>>> %timeit x.startswith(y)
1000000 loops, best of 3: 221 ns per loop
>>> %timeit x[:3] == y  # note: length mismatch
10000000 loops, best of 3: 122 ns per loop
>>> %timeit x[:4] == y
10000000 loops, best of 3: 158 ns per loop
>>> %timeit x[:len(y)] == y
1000000 loops, best of 3: 210 ns per loop
>>> sw = x.startswith
>>> %timeit sw(y)
10000000 loops, best of 3: 176 ns per loop

Aussi, pour une grande partie des chaînes plus longues, startswith est beaucoup plus rapide:

>>> import random
>>> import string
>>> x = '%030x' % random.randrange(256**10000)
>>> len(x)
20000
>>> y = r[:4000]
>>> %timeit x.startswith(y)
1000000 loops, best of 3: 211 ns per loop
>>> %timeit x[:len(y)] == y
1000000 loops, best of 3: 469 ns per loop
>>> sw = x.startswith
>>> %timeit sw(y)
10000000 loops, best of 3: 168 ns per loop

C'est toujours le cas quand il n'y a pas de match.

# change last character of y
>>> y = y[:-1] + chr((ord(y[-1]) + 1) % 256)
>>> %timeit x.startswith(y)
1000000 loops, best of 3: 210 ns per loop
>>> %timeit x[:len(y)] == y
1000000 loops, best of 3: 470 ns per loop
>>> %timeit sw(y)
10000000 loops, best of 3: 168 ns per loop
# change first character of y
>>> y = chr((ord(y[0]) + 1) % 256) + y[1:]
>>> %timeit x.startswith(y)
1000000 loops, best of 3: 210 ns per loop
>>> %timeit x[:len(y)] == y
1000000 loops, best of 3: 442 ns per loop
>>> %timeit sw(y)
10000000 loops, best of 3: 168 ns per loop

Donc, startswith est probablement le plus lent pour les chaînes courtes, car il est optimisé pour les longues.

(Astuce pour obtenir des chaînes aléatoires prises à partir de cette réponse.)

9voto

LastCoder Points 10027

startswith est plus complexe que le tranchage...

2924 result = _string_tailmatch(self,
2925 PyTuple_GET_ITEM(subobj, i),
2926 start, end, -1);

Ce n'est pas un simple personnage de comparer boucle de l'aiguille au début de la botte de foin qui se passe. Nous sommes à la recherche à une boucle est une itération par l'intermédiaire d'un vecteur/n-uplet (subobj) et l'appel d'une autre fonction (_string_tailmatch) sur celui-ci. Plusieurs appels de fonction ont une surcharge en ce qui concerne la pile, l'argument des contrôles d'intégrité etc...

startswith est une fonction de la bibliothèque, tandis que le découpage semble être construit dans la langue.

2919 if (!stringlib_parse_args_finds("startswith", args, &subobj, &start, &end))
2920 return NULL;

8voto

Eric Points 36290

Pour citer les docs, startswith ne plus vous pourriez penser:

str.startswith(prefix[, start[, end]])

De retour True si la chaîne commence par le préfixe, sinon retour False. préfixe peut aussi être un tuple de préfixes à chercher. Avec en option démarrer, test de chaîne commençant à la position. Avec, en option, fin, arrêter la comparaison de chaîne à ce poste.

0voto

glglgl Points 35668

L'appel d'une fonction est assez cher. Je ne sais pas, cependant, si c'est le cas aussi bien pour les fonctions intégrées écrit en C.

Soyez conscient, cependant, que le découpage peut impliquer un appel de fonction en tant que bien, selon l'objet qui est utilisé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X