Mientras perfilaba un fragmento de código Python ( python 2.6
hasta 3.2
), descubrí que el
str
mé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?
python
string
performance
python-3.x
python-2.7
Luca Sbardella
fuente
fuente
'{}'.format(100000)
Respuestas:
'%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 questr
:>>> Timer('str(x)', 'x=100').timeit() 0.25641703605651855 >>> Timer('"%s" % x', 'x=100').timeit() 0.2169809341430664
Tenga en cuenta que
str
todavía es un poco más lento, como dijo @DietrichEpp, esto se debe a questr
implica 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.
fuente
str
. Gracias por responder. No hay razón para cambiar el código en todas partes :-)str
es un nombre que se puede vincular a algo diferente al tipo de cadena, pero el formato de cadena, es decir, elstr.__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 :)Una razón que me viene a la mente es el hecho de que
str(100000)
implica una búsqueda global, pero"%s"%100000
no es así. Lostr
global 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
fuente