Estaba probando las velocidades de algunas maneras diferentes de hacer iteraciones complejas sobre algunos de mis datos, y encontré algo raro. Parece que tener una gran lista local para alguna función ralentiza considerablemente esa función, incluso si no está tocando esa lista. Por ejemplo, la creación de 2 listas independientes a través de 2 instancias de la misma función del generador es aproximadamente 2.5 veces más lenta la segunda vez. Si se elimina la primera lista antes de crear la segunda, ambos iteradores van en el mismo spee.La función de Python se ralentiza con la presencia de una lista grande
def f():
l1, l2 = [], []
for c1, c2 in generatorFxn():
l1.append((c1, c2))
# destroying l1 here fixes the problem
for c3, c4 in generatorFxn():
l2.append((c3, c4))
Las listas terminan alrededor de 3,1 millones de artículos de largo cada uno, pero vi el mismo efecto con listas más pequeñas también. El primer ciclo for
tarda unos 4,5 segundos en ejecutarse, el segundo tarda 10,5. Si inserto l1= []
o l1= len(l1)
en la posición de comentario, ambos bucles for
tardan 4.5 segundos.
¿Por qué la velocidad de asignación de memoria local en una función tiene algo que ver con el tamaño actual de las variables de esa función?
EDITAR: Al deshabilitar el recolector de basura arregla todo, por lo que debe ser debido a que se ejecuta constantemente. ¡Caso cerrado!
Correcto, señor, deshabilitarlo cae 4.5 segundos a 1.3 segundos, y casi elimina las diferencias (el segundo sigue siendo un poco más lento, pero no mucho más). ¿Por qué el recolector de basura funciona tan lento incluso después de una gran lista y solo existe, no se modifica? ¿No debería tener solo un trabajo que hacer una vez que la función regrese? – DaveTheScientist
Además, ¿por qué desaparece la desaceleración si 'l' es una variable de clase en lugar de una variable local? – DaveTheScientist
Aquí está mi mejor estimación: el recolector de basura se ejecuta periódicamente para recolectar objetos viejos. Determina cuándo ejecutar buscando comparando el número de objetos asignados y desasignados desde la última colección. Si el umbral asignado-desasignado>, el recopilador se ejecuta (y el umbral = 700 por defecto). Como crea (al menos) 1 objeto nuevo por iteración, el recopilador ejecuta 3e6/700 = 4285 veces. En realidad, esto ralentiza ambas iteraciones, pero la segunda iteración es más lenta ya que hay más objetos para que el recopilador los compruebe. – Luke