El paralelismo en Python no funciona bien

Estaba desarrollando una aplicación en gae usando python 2.7, una llamada ajax solicita algunos datos de una API, una sola solicitud podría demorar unos 200 ms, sin embargo, cuando abro dos navegadores y hago dos solicitudes muy cerca, toman más que El doble de eso, he intentado poner todo en hilos pero no funcionó … (esto sucede cuando la aplicación está en línea, no solo en el servidor dev)

Así que escribí esta prueba simple para ver si esto es un problema en Python en general (en caso de una espera ocupada), aquí está el código y el resultado:

def work(): t = datetime.now() print threading.currentThread(), t i = 0 while i < 100000000: i+=1 t2 = datetime.now() print threading.currentThread(), t2, t2-t if __name__ == '__main__': print "single threaded:" t1 = threading.Thread(target=work) t1.start() t1.join() print "multi threaded:" t1 = threading.Thread(target=work) t1.start() t2 = threading.Thread(target=work) t2.start() t1.join() t2.join() 

El resultado en mac os x, core i7 (4 núcleos, 8 hilos), python2.7:

 single threaded:  2011-12-06 15:38:07.763146  2011-12-06 15:38:13.091614 0:00:05.328468 multi threaded:  2011-12-06 15:38:13.091952  2011-12-06 15:38:13.102250  2011-12-06 15:38:29.221050 0:00:16.118800  2011-12-06 15:38:29.237512 0:00:16.145560 

¡Esto es bastante impactante! si un solo hilo tomara 5 segundos para hacer esto … pensé que comenzar dos hilos al mismo tiempo tomaría el mismo tiempo para terminar ambas tareas, pero casi demora el tiempo … esto hace que toda la idea de hilo sea inútil, ya que ¡Sería más rápido hacerlas secuencialmente!

que me estoy perdiendo aqui..

David Beazley dio una charla sobre este problema en PyCon 2010. Como ya lo han dicho otros, para algunas tareas, el uso de subprocesos especialmente con múltiples núcleos puede llevar a un rendimiento más lento que la misma tarea realizada por un solo subproceso. El problema, encontró Beazley, tenía que ver con que varios núcleos tuvieran una “batalla GIL” :

introduzca la descripción de la imagen aquí

Para evitar la contención de GIL, puede obtener mejores resultados al ejecutar las tareas en procesos separados en lugar de subprocesos separados. El módulo de multiprocesamiento proporciona una manera conveniente de hacerlo, especialmente porque la API de multiprocesamiento es muy similar a la API de subprocesos.

 import multiprocessing as mp import datetime as dt def work(): t = dt.datetime.now() print mp.current_process().name, t i = 0 while i < 100000000: i+=1 t2 = dt.datetime.now() print mp.current_process().name, t2, t2-t if __name__ == '__main__': print "single process:" t1 = mp.Process(target=work) t1.start() t1.join() print "multi process:" t1 = mp.Process(target=work) t1.start() t2 = mp.Process(target=work) t2.start() t1.join() t2.join() 

rendimientos

 single process: Process-1 2011-12-06 12:34:20.611526 Process-1 2011-12-06 12:34:28.494831 0:00:07.883305 multi process: Process-3 2011-12-06 12:34:28.497895 Process-2 2011-12-06 12:34:28.503433 Process-2 2011-12-06 12:34:36.458354 0:00:07.954921 Process-3 2011-12-06 12:34:36.546656 0:00:08.048761 

PD. Como señaló zeekay en los comentarios, la batalla de GIL solo es grave para las tareas relacionadas con la CPU. No debería ser un problema para las tareas enlazadas a IO.

el intérprete de CPython no permitirá que se ejecute más de un hilo. lea sobre GIL http://wiki.python.org/moin/GlobalInterpreterLock

Por lo tanto, ciertas tareas no se pueden realizar simultáneamente de manera eficiente en el CPython con subprocesos.

Si desea hacer cosas paralelas en GAE, entonces comience de forma paralela con solicitudes separadas.

Además, puede consultar el wiki paralelo de Python http://wiki.python.org/moin/ParallelProcessing

Miraría hacia dónde va el tiempo. Supongamos, por ejemplo, que el servidor solo puede responder a una consulta cada 200 ms. Entonces no hay nada que pueda hacer, solo obtendrá una respuesta cada 200 ms porque eso es todo lo que el servidor puede proporcionarle.