rendimiento de unión de cadenas de Python

Hay muchos artículos en la web sobre el rendimiento de Python, lo primero que lee: concatenar cadenas no debe hacerse usando ‘+’: evite s1 + s2 + s3, en su lugar use str.join

Intenté lo siguiente: concatenar dos cadenas como parte de una ruta de directorio: tres enfoques:

  1. ‘+’ que no debo hacer
  2. str.join
  3. os.path.join

Aquí está mi código:

import os,time s1='/part/one/of/dir' s2='part/two/of/dir' N=10000 t=time.clock() for i in xrange(N): s=s1+os.sep+s2 print time.clock()-t t=time.clock() for i in xrange(N): s=os.sep.join((s1,s2)) print time.clock()-t t=time.clock() for i in xrange(N): s=os.path.join(s1,s2) print time.clock()-t 

Aquí los resultados (python 2.5 WinXP)

 0.0182201927899 0.0262544541275 0.120238186697 

¿No debería ser exactamente al revés?

Es cierto que no debes usar ‘+’. Tu ejemplo es bastante especial, prueba el mismo código con:

 s1='*'*100000 s2='+'*100000 

Entonces la segunda versión (str.join) es mucho más rápida.

La mayoría de los problemas de rendimiento con la concatenación de cadenas son de rendimiento asintótico, por lo que las diferencias se vuelven más significativas cuando se concatenan muchas cadenas largas. En su muestra, está realizando la misma concatenación muchas veces. No está construyendo una cadena larga, y puede ser que el intérprete de python esté optimizando sus bucles. Esto explicaría por qué el tiempo aumenta cuando te mueves hacia str.join y path.join; son funciones más complejas que no se reducen tan fácilmente. (os.path.join hace una gran cantidad de control en las cadenas para ver si necesitan ser reescritas de alguna manera antes de concatenarlas. Esto sacrifica algo de rendimiento en aras de la portabilidad).

Por cierto, como las rutas de los archivos no suelen ser muy largas, es casi seguro que desee utilizar os.path.join por el bien de la portabilidad. Si el rendimiento de la concatenación es un problema, está haciendo algo muy extraño con su sistema de archivos.

¿No debería ser exactamente al revés?

No necesariamente. No conozco lo suficiente sobre los componentes internos de Python para comentar específicamente, pero algunas observaciones comunes son que su primer bucle utiliza un operador simple + que se implementa de forma proactiva como primitivo por el tiempo de ejecución. En contraste, los otros bucles primero tienen que resolver el nombre de un módulo, resolver la variable / clase que se encuentra allí y luego llamar a una función miembro.

Otra nota es que su bucle puede ser simplemente demasiado pequeño para producir números significativos. Teniendo en cuenta su bajo tiempo de ejecución en general, esto probablemente hace que sus pruebas sean inútiles.

Además, su caso de prueba es altamente especializado en dos cadenas cortas. Tales casos nunca dan una imagen clara del rendimiento de casos de borde.

El consejo es acerca de concatenar muchas cadenas.

Para calcular s = s1 + s2 + … + sn,

1) utilizando +. Se crea una nueva cadena s1 + s2, luego se crea una nueva cadena s1 + s2 + s3, …, etc., por lo que se requiere mucha asignación de memoria y operaciones de copia. De hecho, s1 se copia n-1 veces, s2 se copia n-2 vez, …, etc.

2) utilizando “” .join ([s1, s2, …, sn]). La concatenación se realiza en una sola pasada, y cada carácter en las cadenas se copia una sola vez.

En su código, la unión se llama en cada iteración, por lo que es como usar +. La forma correcta es recolectar los elementos en una matriz, luego llamar a unirse en ella.

edición: arreglado el error tipográfico

La concatenación de cadenas ( + ) tiene una implementación optimizada en CPython. Pero este puede no ser el caso en otras architectures como Jython o IronPython. Por lo tanto, cuando desee que su código .join() bien en estos intérpretes, debe usar el método .join() en cadenas. os.path.join() está diseñado específicamente para unirse a las rutas del sistema de archivos. Se ocupa de separadores de caminos diferentes, también. Esta sería la forma correcta de construir un nombre de archivo.

Me gustaría agregar un enlace a la wiki de python, donde hay notas sobre la concatenación de cadenas y también que ” esta sección es un poco incorrecta con python2.5. La concatenación de cadenas de Python 2.5 es bastante rápida “.

Creo que la concatenación de cadenas tuvo una gran mejora desde la versión 2.5, y aunque str.join es aún más rápida (especialmente para cadenas grandes), no verá tanta mejora como en las versiones anteriores de Python.

http://wiki.python.org/moin/PythonSpeed/PerformanceTips#StringConcatenation