2012-01-09 23 views
7

Escribí unas 50 clases que uso para conectarme y trabajar con sitios web usando mecanizar y enhebrar. Todos trabajan al mismo tiempo, pero no dependen el uno del otro. Entonces 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 datos recuperados en medio de métodos - como 'inicio de sesión' - que otros podrían no necesitar). Como dije, no es elegante, pero funciona. Huelga decir que doy la bienvenida a todas las recomendaciones sobre cómo escribir mejor sin utilizar 1 clase para cada enfoque de sitio web. Agregar funciones adicionales o la administración general del código de cada clase es una tarea desalentadora.Cómo reducir el uso de memoria del código python enhebrado?

Sin embargo, me enteré de que cada subproceso requiere aproximadamente 8 MB de memoria, por lo que con 50 subprocesos estamos buscando aproximadamente 400 MB de uso. Si se ejecutara en mi sistema, no tendría problemas con eso, pero dado que se está ejecutando en un VPS con solo 1GB de memoria, está empezando a ser un problema. ¿Puede decirme cómo reducir el uso de memoria, o hay alguna otra forma de trabajar con múltiples sitios al mismo tiempo?

Utilicé este programa de prueba rápida python para comprobar si son los datos almacenados en variables de mi aplicación que está usando la memoria, o alguna otra cosa. Como puede ver en el siguiente código, solo está procesando la función sleep(), pero cada hilo está usando 8MB 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 quitando running = False línea, que puede luego medir memoria libre usando free -m comando en la shell:

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

El actual El cálculo de por qué sé que está tomando alrededor de 8 MB por hilo es simple al dividir dividiendo la diferencia de memoria utilizada antes y durante la ejecución de la aplicación de prueba anterior. g, dividido por los hilos máximos que logró comenzar.

Probablemente solo tenga memoria asignada, ya que al mirar top, el proceso de python usa solo aproximadamente el 0.6% de la memoria.

+0

¿Qué está ocupando la memoria? Me atrevería a adivinar que son los datos que extraes de los sitios. Si ese es el caso, entonces probablemente no haya mucho que puedas hacer si no acelera el número de subprocesos de ejecución. –

+0

¿Cómo se mide exactamente el uso de la memoria? Supongo que esos 8MB realmente no están asignados a cada hilo. Una gran parte de esos 8MB puede ser compartida entre los hilos (solo una conjetura ...)? – Frunsi

+0

Demian y frunsi, edité mi pregunta para responder a ambas preguntas. ¡Gracias! – Gargauth

Respuesta

0

No soy un experto en Python, pero tal vez tenga algunos grupos de hilos que controlan el número total de hilos activos y entrega una 'solicitud' a un hilo una vez hecho con el hilo anterior. La solicitud no tiene que ser el objeto de la secuencia completa, solo los datos suficientes para completar la solicitud.

También podría estructurarlo para que tenga el grupo de subprocesos A con N subprocesos que hacen ping al sitio web, una vez que se recuperan los datos, transfiéralos de los datos al grupo de subprocesos B con subprocesos Y crujiendo los datos.

2

Usar "un hilo por solicitud" es correcto y fácil para muchos casos de uso. Sin embargo, requerirá muchos recursos (como lo experimentó).

Un mejor enfoque es usar uno asincrónico, pero desafortunadamente es mucho más complejo.

Algunos consejos en esta dirección:

+0

Gracias, muy apreciado. Leí acerca de Twisted antes, pero lamentablemente no sé mucho al respecto y, por lo que parece, no podría usar mecanizar con él. Voy a echar un vistazo si podría hacer que mecanizar funcione con asyncore. – Gargauth

+0

Después de todo, una solución "perfecta" sería una combinación de grupos de subprocesos con un subproceso por núcleo de CPU (para utilizarlos para tareas de procesamiento) y IO asincrónico. Una solución práctica dependerá de su código de aplicación real. Quizás, incluso una solución simple basada en 'select' lo haga por usted. – Frunsi

+1

Esto significa: en su hilo: envíe un montón de solicitudes, luego ingrese un bucle que 'seleccionará' en los sockets apropiados, y manejará los datos entrantes uno por uno ... y así sucesivamente. Después de todo, el sistema operativo se preocupa por el socket IO de todos modos, su tarea es interactuar con el sistema operativo de la manera más eficiente posible. – Frunsi

1

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

1) Hacer algo .
2) Espere a que pase algo.
3) Haz otra cosa.

Con código como este:

1) hacer algo.
2) Dispóngalo de modo que cuando algo suceda, se haga algo más.
3) Hecho.

En otro lugar, usted tiene unos hilos que hacen esto:

1) Esperar a que pase algo.
2) Maneje lo que sucedió.
3) Vaya al paso 1.

En el primer caso, si está esperando que sucedan 50 cosas, tiene 50 hilos esperando a que pasen 50 cosas. En el segundo caso, tiene un hilo esperando que hará cualquiera de esas 50 cosas que se deben hacer.

Por lo tanto, no utilice un hilo para esperar que ocurra una sola cosa. En cambio, organízala de modo que cuando eso suceda, algún otro hilo haga lo que sea necesario hacer a continuación.

Cuestiones relacionadas