web-dev-qa-db-fra.com

Pourquoi str.translate est-il beaucoup plus rapide dans Python 3.5 comparé à Python 3.4?)?

J'essayais de supprimer les caractères indésirables d'une chaîne donnée en utilisant text.translate() in Python 3.4.

Le code minimal est:

import sys 
s = 'abcde12345@#@$#%$'
mapper = dict.fromkeys(i for i in range(sys.maxunicode) if chr(i) in '@#$')
print(s.translate(mapper))

Cela fonctionne comme prévu. Cependant, le même programme lorsqu'il est exécuté dans Python 3.4 et Python 3.5 donne une grande différence.

Le code pour calculer les timings est

python3 -m timeit -s "import sys;s = 'abcde12345@#@$#%$'*1000 ; mapper = dict.fromkeys(i for i in range(sys.maxunicode) if chr(i) in '@#$'); "   "s.translate(mapper)"

Le programme Python 3.4 prend 1,3 ms alors que le même programme est Python 3.5 ne prend que 26.4μs .

Qu'est-ce qui s'est amélioré dans Python 3.5 qui le rend plus rapide que Python 3.4?

116
Bhargav Rao

TL; DR - NUMÉRO 21118


La longue histoire

Josh Rosenberg a découvert que la fonction str.translate() est très lente par rapport au bytes.translate. Il a soulevé un issue , déclarant que:

Dans Python 3, str.translate() est généralement une pessimisation des performances, pas une optimisation.

Pourquoi str.translate() était-il lent?

La raison principale pour que str.translate() soit très lente était que la recherche était auparavant dans un dictionnaire Python.

L'utilisation de maketrans a aggravé le problème. L'approche similaire utilisant bytes construit un tableau C de 256 éléments pour une recherche rapide dans les tables. D'où l'utilisation du niveau supérieur Python dict rend la str.translate() dans Python 3.4 très lente.

Qu'est-il arrivé maintenant?

La première approche consistait à ajouter un petit patch, translate_writer , Cependant, l’augmentation de la vitesse n’était pas aussi agréable. Bientôt un autre patch fast_translate a été testé et il a donné de très bons résultats, avec une accélération jusqu’à 55%.

Le principal changement constaté dans le fichier est que la recherche dans le dictionnaire Python est modifiée en une recherche au niveau C.

Les vitesses sont maintenant presque les mêmes que bytes

                                unpatched           patched

str.translate                   4.55125927699919    0.7898181750006188
str.translate from bytes trans  1.8910855210015143  0.779950579000797

Une petite remarque ici est que l'amélioration des performances est uniquement visible dans les chaînes ASCII.

Comme J.F.Sebastian le mentionne dans un commentaire ci-dessous, avant 3.5, la traduction fonctionnait de la même manière pour les cas ASCII et non ASCII. Cependant à partir de 3.5 ASCII la casse est beaucoup plus rapide.

Auparavant, ASCII vs non-ASCII était presque identique, mais nous pouvons maintenant constater un grand changement dans les performances.

Cela peut être une amélioration de 71,6 µs à 2,33 µs, comme indiqué dans ce réponse .

Le code suivant montre ceci

python3.5 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"
100000 loops, best of 3: 2.3 usec per loop
python3.5 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"
10000 loops, best of 3: 117 usec per loop

python3 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"
10000 loops, best of 3: 91.2 usec per loop
python3 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"
10000 loops, best of 3: 101 usec per loop

Tableau des résultats:

         Python 3.4    Python 3.5  
Ascii     91.2          2.3 
Unicode   101           117
150
Bhargav Rao