str rendimiento en python


88

Mientras perfilaba un fragmento de código Python ( python 2.6hasta 3.2), descubrí que el strmétodo para convertir un objeto (en mi caso, un número entero) en una cadena es casi un orden de magnitud más lento que usar el formato de cadena.

Aquí está el punto de referencia

>>> from timeit import Timer
>>> Timer('str(100000)').timeit()
0.3145311339386332
>>> Timer('"%s"%100000').timeit()
0.03803517023435887

¿Alguien sabe por qué es así? ¿Me estoy perdiendo de algo?


2
Y qué tal'{}'.format(100000)
wim

Ese es el más lento pero también el más flexible.
Luca Sbardella

Respuestas:


106

'%s' % 100000 es evaluado por el compilador y es equivalente a una constante en tiempo de ejecución.

>>> import dis
>>> dis.dis(lambda: str(100000))
  8           0 LOAD_GLOBAL              0 (str)
              3 LOAD_CONST               1 (100000)
              6 CALL_FUNCTION            1
              9 RETURN_VALUE        
>>> dis.dis(lambda: '%s' % 100000)
  9           0 LOAD_CONST               3 ('100000')
              3 RETURN_VALUE        

%con una expresión en tiempo de ejecución no es (significativamente) más rápido que str:

>>> Timer('str(x)', 'x=100').timeit()
0.25641703605651855
>>> Timer('"%s" % x', 'x=100').timeit()
0.2169809341430664

Tenga en cuenta que strtodavía es un poco más lento, como dijo @DietrichEpp, esto se debe a que strimplica operaciones de búsqueda y llamada de función, mientras que se %compila en un solo código de bytes inmediato:

>>> dis.dis(lambda x: str(x))
  9           0 LOAD_GLOBAL              0 (str)
              3 LOAD_FAST                0 (x)
              6 CALL_FUNCTION            1
              9 RETURN_VALUE        
>>> dis.dis(lambda x: '%s' % x)
 10           0 LOAD_CONST               1 ('%s')
              3 LOAD_FAST                0 (x)
              6 BINARY_MODULO       
              7 RETURN_VALUE        

Por supuesto, lo anterior es cierto para el sistema en el que probé (CPython 2.7); otras implementaciones pueden diferir.


De hecho, esta parece ser la razón, lo intenté yo mismo y el formato de cadena es aproximadamente un 5% más rápido que str. Gracias por responder. No hay razón para cambiar el código en todas partes :-)
Luca Sbardella

2
Para desarrollar más: stres un nombre que se puede vincular a algo diferente al tipo de cadena, pero el formato de cadena, es decir, el str.__mod__método, no se puede reemplazar, lo que permite que el compilador realice la optimización. El compilador no hace mucho en cuanto a optimización, pero hace más de lo que podría pensar :)
Karl Knechtel

4
... y la lección que debe aprender aquí es: ¡nunca use literales en pruebas como estas!
UncleZeiv

Esta entrada de blog en particular puede interesarle: skymind.com/~ocrow/python_string . Contiene una tabla de puntos de referencia para varios métodos de concatenación de cadenas similar a lo que proporcionó anteriormente.
Aaron Newton

14

Una razón que me viene a la mente es el hecho de que str(100000)implica una búsqueda global, pero "%s"%100000no es así. Lo strglobal debe buscarse en el ámbito global. Esto no explica toda la diferencia:

>>> Timer('str(100000)').timeit()
0.2941889762878418
>>> Timer('x(100000)', 'x=str').timeit()
0.24904918670654297

Como señaló thg435 ,

>>> Timer('"%s"%100000',).timeit()
0.034214019775390625
>>> Timer('"%s"%x','x=100000').timeit()
0.2940788269042969
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.