32 votes

Pourquoi str.strip () est-il tellement plus rapide que str.strip ('')?

La séparation d'espace blanc peut être fait de deux manières, avec str.strip. Vous pouvez émettre un appel sans argument, str.strip(), qui est par défaut à l'aide d'un white-space délimiteur ou explicitement, fournissez l'argument-vous avec str.strip(' ').

Mais, pourquoi est-il que lorsque chronométré ces fonctions effectuent de façon différente?

À l'aide d'un exemple de chaîne avec préméditation montant des espaces blancs:

s = " " * 100 + 'a' + " " * 100

Les horaires s.strip() et s.strip(' ') sont respectivement:

%timeit s.strip()
The slowest run took 32.74 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 396 ns per loop

%timeit s.strip(' ')
100000 loops, best of 3: 4.5 µs per loop

strip faut 396ns tout strip(' ') faut 4.5 μs, un scénario similaire est présent avec rsplit et lsplit dans les mêmes conditions. Aussi, bytes objects semblent n'en être affecté.

Les horaires ont été effectuées pour l' Python 3.5.2sur Python 2.7.1 , la différence est moins drastique. Les docs sur str.split n'indiquent en rien d'utile, donc, pourquoi est-ce possible?

35voto

Jim Points 8793

Dans un tl;dr mode:

C'est parce que les deux fonctions existent pour les deux affaires différentes, comme on peut le voir en unicode_strip; do_strip et _PyUnicodeXStrip de la première exécution beaucoup plus rapide que la seconde.

La fonction do_strip est pour le cas courant str.strip() où aucun des arguments existent et do_argstrip (qui s'enroule _PyUnicode_XStrip) pour le cas où l' str.strip(arg) est appelé, j'.e arguments sont fournis.


do_argstrip vérifie juste le séparateur et si elle est valide et n'est pas égal à None (auquel cas il appelle do_strip) il appelle _PyUnicode_XStrip.

Les deux do_strip et _PyUnicode_XStrip suivre la même logique, les deux compteurs sont utilisés, l'un égal à zéro et l'autre égal à la longueur de la chaîne.

À l'aide de deux while boucles, le premier compteur est incrémenté jusqu'à ce qu'une valeur n'a d'égale que le séparateur est atteint et que le deuxième compteur est décrémenté jusqu'à ce que la même condition soit remplie.

La différence réside dans la façon de vérifier si le caractère actuel n'est pas égale à la séparation est effectuée.

Pour do_strip:

Dans le cas le plus fréquent où les caractères de la chaîne à split peut être représenté en ascii un petit boost de performance est présent.

while (i < len) {
    Py_UCS1 ch = data[i];
    if (!_Py_ascii_whitespace[ch])
        break;
    i++;
}
  • Accéder à l'actualité des données est réalisé rapidement, en accédant au sous-jacents au tableau: Py_UCS1 ch = data[i];
  • Le vérifier si un caractère est un espace blanc est faite par un simple index de tableau dans un tableau appelé _Py_ascii_whitespace[ch].

Donc, en bref, c'est assez efficace.

Si les personnages ne sont pas dans l' ascii gamme, les différences ne sont pas aussi drastiques mais ils ne ralentissent l'exécution globale vers le bas:

while (i < len) {
    Py_UCS4 ch = PyUnicode_READ(kind, data, i);
    if (!Py_UNICODE_ISSPACE(ch))
        break;
    i++;
}
  • L'accès se fait avec Py_UCS4 ch = PyUnicode_READ(kind, data, i);
  • Vérifier si le caractère est une espace est fait par l' Py_UNICODE_ISSPACE(ch) macro (qui appelle tout simplement une autre macro: Py_ISSPACE)

Pour _PyUnicodeXStrip:

Pour ce cas, l'accès aux données sous-jacentes sont, comme dans le cas précédent, avec l' PyUnicode_Read; le chèque, sur l'autre main, pour voir si le caractère est un espace blanc (ou tout autre caractère que nous avons fourni) est raisonnablement un peu plus complexe.

while (i < len) {
     Py_UCS4 ch = PyUnicode_READ(kind, data, i);
     if (!BLOOM(sepmask, ch))
         break;
     if (PyUnicode_FindChar(sepobj, ch, 0, seplen, 1) < 0)
         break;
     i++;
}

PyUnicode_FindChar est utilisé, ce qui, bien qu'efficace, est beaucoup plus complexe et plus lent par rapport à une matrice d'accès. Pour chaque caractère dans la chaîne, il est appelé pour voir si ce caractère est contenue dans le séparateur(s) que nous avons fournis. Comme la longueur de la chaîne augmente, l'overhead introduit par l'appel de cette fonction en permanence.

Pour ceux qui sont intéressés, PyUnicode_FindChar après quelques vérifications, finira par appel d' find_char à l'intérieur d' stringlib qui, dans le cas où la longueur des séparateurs < 10 boucle jusqu'à ce qu'il trouve le personnage.

En dehors de cela, considérons les fonctions supplémentaires qui doivent déjà être appelé pour obtenir ici.


Comme pour lstrip et rstrip, la situation est similaire. Des drapeaux pour le mode d'entrelacement pour effectuer existent, à savoir: RIGHTSTRIP pour rstrip, LEFTSTRIP pour lstrip et BOTHSTRIP pour strip. La logique à l'intérieur d' do_strip et _PyUnicode_XStrip est effectuée de façon conditionnelle basée sur le pavillon.

7voto

root Points 344

Pour les raisons expliquées dans @Jims, le même comportement se retrouve dans les objets bytes :

 b = bytes(" " * 100 + "a" + " " * 100, encoding='ascii')

b.strip()      # takes 427ns
b.strip(b' ')  # takes 1.2μs
 

Cela ne se produit pas pour les objets bytearray , les fonctions effectuant les split dans ce cas sont similaires dans les deux cas.

De plus, en Python 2 la même chose s'applique dans une moindre mesure en fonction de mes horaires.

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