¿Cómo reducir el uso de memoria del código de python con hilos?

Escribí unas 50 clases que utilizo para conectar y trabajar con sitios web utilizando mecanizar y subprocesos. Todos trabajan al mismo tiempo, pero no dependen unos de otros. Así que eso significa 1 clase – 1 sitio web – 1 hilo. No es una solución particularmente elegante, especialmente para administrar el código, ya que gran parte del código se repite en cada clase (pero no lo suficiente como para convertirlo en una clase para pasar argumentos, ya que algunos sitios pueden requerir procesamiento adicional de los datos recuperados en medio de los métodos). – como ‘login’ – que otros podrían no necesitar). Como dije, no es elegante, pero funciona. No hace falta decir que agradezco todas las recomendaciones sobre cómo escribir esto mejor sin usar 1 clase para cada enfoque de sitio web. Agregar una funcionalidad adicional o la administración general del código de cada clase es una tarea desalentadora.

Sin embargo, descubrí que cada subproceso toma aproximadamente 8 MB de memoria, por lo que con 50 subprocesos en ejecución estamos considerando un uso de aproximadamente 400 MB. Si estuviera funcionando en mi sistema, no tendría ningún problema con eso, pero como se ejecuta en un VPS con solo 1 GB de memoria, está empezando a ser un problema. ¿Puede decirme cómo reducir el uso de la memoria, o hay alguna otra forma de trabajar con varios sitios al mismo tiempo?

Utilicé este progtwig de prueba rápida de python para comprobar si los datos almacenados en las variables de mi aplicación utilizan la memoria o alguna otra cosa. Como puede ver en el siguiente código, solo está procesando la función sleep (), sin embargo, cada subproceso utiliza 8 MB de memoria.

from thread import start_new_thread from time import sleep def sleeper(): try: while 1: sleep(10000) except: if running: raise def test(): global running n = 0 running = True try: while 1: start_new_thread(sleeper, ()) n += 1 if not (n % 50): print n except Exception, e: running = False print 'Exception raised:', e print 'Biggest number of threads:', n if __name__ == '__main__': test() 

Cuando ejecuto esto, la salida es:

 50 100 150 Exception raised: can't start new thread Biggest number of threads: 188 

Y eliminando running = False line, puedo medir la memoria free -m usando free -m comando free -m en el shell:

  total used free shared buffers cached Mem: 1536 1533 2 0 0 0 -/+ buffers/cache: 1533 2 Swap: 0 0 0 

El cálculo real por el que sé que toma aproximadamente 8 MB por subproceso es simple dividiendo la división de la diferencia de memoria utilizada antes y durante la ejecución de la aplicación de prueba anterior, dividida por el máximo de subprocesos que logró iniciar.

Es probable que solo se asigne memoria, porque al mirar hacia top , el proceso de Python utiliza solo un 0,6% de la memoria.

  • usar menos hilos: ejemplo de ThreadPoolExecutor . Instalar futures en Python 2.x
  • intente el enfoque asíncrono:
    • ejemplo de gevent
    • ejemplo retorcido

El uso de “un hilo por solicitud” está bien y es fácil para muchos casos de uso. Sin embargo, requerirá una gran cantidad de recursos (como usted experimentó).

Un mejor enfoque es usar uno asíncrono, pero desafortunadamente es mucho más complejo.

Algunos consejos en esta dirección:

  • asyncore
  • Retorcido

La solución es reemplazar código como este:

1) Hacer algo.
2) Espera que algo suceda.
3) Hacer otra cosa.

Con código como este:

1) Hacer algo.
2) Organízalo para que cuando algo suceda, se haga otra cosa.
3) Hecho.

En otro lugar, tienes algunos hilos que hacen esto:

1) Espera que pase algo.
2) Manejar lo que haya pasado.
3) Vaya al paso 1.

En el primer caso, si está esperando que sucedan 50 cosas, tiene 50 hilos sentados alrededor esperando que sucedan 50 cosas. En el segundo caso, tiene un hilo de espera que hará cualquiera de las 50 cosas que deben hacerse.

Por lo tanto, no use un hilo para esperar que suceda una sola cosa. En su lugar, organícelo de modo que cuando eso suceda, algún otro subproceso hará lo que deba hacerse a continuación.

No soy un experto en Python, pero tal vez tenga algunos grupos de subprocesos que controlan el número total de subprocesos activos, y entrega una ‘solicitud’ a un subproceso una vez que se hace con el subproceso anterior. La solicitud no tiene que ser el objeto de subproceso completo, solo los datos suficientes para completar cualquiera que sea la solicitud.

También puede estructurarlo para que tenga un grupo de subprocesos A con N subprocesos que hagan ping al sitio web, una vez que se recuperen los datos, transfiéralos a la agrupación de subprocesos B con subprocesos Y y procesando los datos.