¿Por qué str.translate es mucho más rápido en Python 3.5 en comparación con Python 3.4?
string python-3.x (1)
text.translate()
eliminar caracteres no deseados de una cadena dada usando
text.translate()
en Python 3.4.
El código mínimo es:
import sys
s = ''abcde12345@#@$#%$''
mapper = dict.fromkeys(i for i in range(sys.maxunicode) if chr(i) in ''@#$'')
print(s.translate(mapper))
Funciona como se esperaba. Sin embargo, el mismo programa cuando se ejecuta en Python 3.4 y Python 3.5 ofrece una gran diferencia.
El código para calcular los tiempos es
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)"
El programa Python 3.4 tarda 1.3ms mientras que el mismo programa en Python 3.5 solo toma 26.4μs .
¿Qué ha mejorado en Python 3.5 que lo hace más rápido en comparación con Python 3.4?
TL; DR - NÚMERO 21118
La larga historia
Josh Rosenberg descubrió que la función
str.translate()
es muy lenta en comparación con los
bytes.translate
, planteó un
problema al
afirmar que:
En Python 3,
str.translate()
suele ser un pesimismo de rendimiento, no una optimización.
¿Por qué
str.translate()
lento?
La razón principal para que
str.translate()
sea muy lenta fue que la búsqueda solía estar en un diccionario de Python.
El uso de
maketrans
este problema.
El enfoque similar que usa
bytes
construye una matriz en C de 256 elementos para acelerar la búsqueda de tablas.
Por lo tanto, el uso de
dict
Python de nivel superior hace que
str.translate()
en Python 3.4 sea muy lento.
¿Que ha pasado ahora?
El primer enfoque fue agregar un pequeño parche, translate_writer . Sin embargo, el aumento de velocidad no fue tan agradable. Pronto se probó otro parche fast_translate y arrojó resultados muy buenos de hasta un 55% de aceleración.
El cambio principal como se puede ver en el archivo es que la búsqueda del diccionario Python se cambia a una búsqueda de nivel C.
Las velocidades ahora son casi las mismas que los
bytes
unpatched patched
str.translate 4.55125927699919 0.7898181750006188
str.translate from bytes trans 1.8910855210015143 0.779950579000797
Una pequeña nota aquí es que la mejora del rendimiento solo es prominente en las cadenas ASCII.
Como JFSebastian menciona en un comment continuación, Antes de 3.5, traducir solía funcionar de la misma manera para los casos ASCII y no ASCII. Sin embargo, desde el caso 3.5 ASCII es mucho más rápido.
Anteriormente ASCII vs no ASCII solía ser casi lo mismo, sin embargo, ahora podemos ver un gran cambio en el rendimiento.
Puede ser una mejora de 71.6μs a 2.33μs como se ve en esta answer .
El siguiente código demuestra esto
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
Tabulación de los resultados:
Python 3.4 Python 3.5
Ascii 91.2 2.3
Unicode 101 117