python string python-3.x python-internals python-3.5

¿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