Algunas medidas de rendimiento, utilizando en timeit
lugar de intentar hacerlo manualmente time
.
Primero, Apple 2.7.2 de 64 bits:
In [37]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.05 s per loop
Ahora, python.org 3.3.0 de 64 bits:
In [83]: %timeit collections.deque((x for x in range(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.32 s per loop
In [84]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.31 s per loop
In [85]: %timeit collections.deque((x for x in iter(range(10000000)) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.33 s per loop
Al parecer, 3.x range
realidad es un poco más lento que 2.x xrange
. Y la xrange
función del OP no tiene nada que ver con eso. (No es sorprendente, ya que __iter__
no es probable que una llamada única a la ranura sea visible entre 10000000 llamadas a lo que ocurra en el bucle, pero alguien lo mencionó como una posibilidad).
Pero es solo un 30% más lento. ¿Cómo llegó el OP 2 veces más lento? Bueno, si repito las mismas pruebas con Python de 32 bits, obtengo 1.58 vs. 3.12. Entonces, supongo que este es otro de esos casos en los que 3.x ha sido optimizado para un rendimiento de 64 bits en formas que perjudican a 32 bits.
pero, realmente importa? Mira esto, con 3.3.0 64 bits nuevamente:
In [86]: %timeit [x for x in range(10000000) if x%4 == 0]
1 loops, best of 3: 3.65 s per loop
Por lo tanto, construir el list
toma más del doble de tiempo que la iteración completa.
Y en cuanto a "consume muchos más recursos que Python 2.6+", según mis pruebas, parece que un 3.x range
tiene exactamente el mismo tamaño que un 2.x xrange
, e, incluso si fuera 10 veces más grande, construir la lista innecesaria sigue siendo aproximadamente 10000000x un problema mayor que cualquier cosa que la iteración de rango pueda hacer.
¿Y qué hay de un for
bucle explícito en lugar del bucle C dentro deque
?
In [87]: def consume(x):
....: for i in x:
....: pass
In [88]: %timeit consume(x for x in range(10000000) if x%4 == 0)
1 loops, best of 3: 1.85 s per loop
Entonces, casi tanto tiempo perdido en la for
declaración como en el trabajo real de iterar el range
.
Si le preocupa optimizar la iteración de un objeto de rango, probablemente esté buscando en el lugar equivocado.
Mientras tanto, sigues preguntando por qué xrange
se eliminó, no importa cuántas veces la gente le diga lo mismo, pero lo repetiré nuevamente: no se eliminó: se cambió el nombre a range
, y el 2.x range
es lo que se eliminó.
Aquí hay alguna prueba de que el range
objeto 3.3 es un descendiente directo del xrange
objeto 2.x (y no de la range
función 2.x ): la fuente de 3.3range
y 2.7xrange
. Incluso puede ver el historial de cambios (vinculado, creo, al cambio que reemplazó la última instancia de la cadena "xrange" en cualquier parte del archivo).
Entonces, ¿por qué es más lento?
Bueno, por un lado, han agregado muchas características nuevas. Por otro lado, han realizado todo tipo de cambios en todo el lugar (especialmente dentro de la iteración) que tienen efectos secundarios menores. Y ha habido mucho trabajo para optimizar dramáticamente varios casos importantes, incluso si a veces pesimiza ligeramente los casos menos importantes. Agregue todo esto, y no me sorprende que iterar lo range
más rápido posible ahora sea un poco más lento. Es uno de esos casos menos importantes en los que nadie se preocuparía lo suficiente. Es probable que nadie tenga un caso de uso real en el que esta diferencia de rendimiento sea el punto clave en su código.
range
en Python 3.x esxrange
de Python 2.x. De hecho, Python 2.x'srange
fue eliminado.