Multiprocesamiento en Python
Multiprocesamiento en Python
Multiprocesamiento en Python
Aprovechar al 100% los multicore modernos con Python Abrirse a la posibilidad de escalar a multi-nodo (clusters) Mantener la facilidad de desarrollo caracterstica de Python
Multiprocesamiento qu es?
Sumar dos arrays de nmeros Calcular promedios Multitud de otras tareas intensivas en cmputo
Multiprocesamiento qu no es?
Formas de multiprocesamiento
Multiproceso
Correr varias instancias separadas del proceso, cada una con una parte del workload Esencialmente como multiproceso, pero:
Multithreading
Corren en el mismo espacio de memoria Se pueden crear ms threads y ms rpido que procesos
Multiproceso
Pipe
RAM P0 CPU0
RAM P1 CPU1
RAM Pn
No comparten nada
CPUn
P0
P1
Pipe
Pn
Multithreading
Queue
Comparten memoria
RAM
CPU0
CPU1
CPUn
Colas Buffers
Requieren sincronizacin
T0
T1
Queue
Tn
Mutexes Semforos
Multithreading en python
Ejemplo
def geturl(url): from urllib import urlopen return urlopen(url).read() u1 = http://www.google.com.ar u2 = http://ar.livra.com from threading import Thread t1 = Thread(target=geturl, args=(u1,)) t2 = Thread(target=geturl, args=(u2,)) t1.start() t2.start()
WTF?
>>> ... ... ... ... >>> >>> >>> >>> >>> def stupid(): x = 0 while True: x = x + 1 from threading import Thread t1 = Thread(target=stupid) t2 = Thread(target=stupid) t1.start() t2.start()
WTF?
>>> ... ... ... ... >>> >>> >>> >>> >>> def stupid(): x = 0 while True: x = x + 1 from multiprocessing import Process t1 = Process(target=stupid) t2 = Process(target=stupid) t1.start() t2.start()
WTF?
>>> ... ... ... ... >>> >>> >>> >>> >>> def stupid(): x = 0 while True: x = x + 1 from multiprocessing import Process t1 = Process(target=stupid) t2 = Process(target=stupid) t1.start() t2.start()
WTF?
Los threads pueden pisarse entre s y producir comportamiento no determinista Si ms de una instancia de CPython corriera al mismo tiempo, probablemente funcionara mal Global Interpreter Lock En python, no pueden correr dos threads a la vez
CPython no es thread-safe
Luego: GIL
GIL
Thread 1 Thread 2
WTF?
Paralelizar I/O
Python libera el GIL cuando est esperando datos de la red o el disco Hay aplicaciones que son ms fciles de expresar como mltiples hilos de ejecucin que como un programa lineal
Simplificar conceptos
WTF?
Paralelizar I/O
Python libera el GIL cuando est esperando datos de la red o el disco Hay aplicaciones que son ms fciles de expresar como mltiples hilos de ejecucin que como un programa lineal Si se conoce el truco correcto
Simplificar conceptos
Paralelizar cmputos
Opciones?
Otras implementacioens PyPy Jython Multiproceso fork multiprocessing XML-RPC MPI Multithreading Pyrex + with nogil
Opciones?
Otras implementacioens PyPy Jython Multiproceso fork multiprocessing XML-RPC MPI Multithreading Pyrex + with nogil
Multiprocessing
Pros:
Cons:
XML-RPC
Pros: Estndar Implementaciones heterogneas Escalable a mltiples nodos Cons: Difcil comunicacin interproceso Alto overhead (ms que multiprocessing) Esquema rgido No es transparente Afecta al diseo de la aplicacin No es un mero detalle de la implementacin
Pros: Optimiza! Pyrex (simil-Python) genera cdigo C y luego cdigo nativo, optimizado por el compilador de C Permite crear estructuras por fuera de python, sin sus ataduras (GIL), e incluso con algo de esfuerzo utilizar las grandes libreras de C++ Por fuera parece python, por dentro es C
Cuando una seccin no necesita tocar estructuras de python, puede liberar el GIL (with nogil), permitiendo paralelismo y verdadero multithreading.
Cons:
Necesita compilacin Hay que tener mucho cuidado con el uso de with nogil Como en C, errores de programacin pueden resultar en segmentation fault Algo que en Python rara vez se ve No es python puro
Pyrex
ejemplo
Ejemplo
_stupid.pyx
def stupid(): cdef int x with nogil: x = 0 while 1: x = x + 1 >>> >>> >>> >>> >>> >>>
Ejemplo
stupid.py
from _stupid import stupid from threading import Thread t1 = Thread(target=stupid) t2 = Thread(target=stupid) t1.start() t2.start()
Pyrex
ejemplo
Ejemplo
_stupid.pyx
def stupid(): cdef int x with nogil: x = 0 while 1: x = x + 1 >>> >>> >>> >>> >>> >>>
Ejemplo
stupid.py
from _stupid import stupid from threading import Thread t1 = Thread(target=stupid) t2 = Thread(target=stupid) t1.start() t2.start()
Secciones de clculo puro pueden liberar el GIL, habilitando verdadero multithreading Incluso podemos leer memoria compartida sin trabar el GIL
Incluso podemos escribir memoria compartida...
MPI
Pros
http://pympi.sourceforge.net/
PyMPI
Cons
MPI
MPI
MPI
MPI
MPI necesita un ambiente especial pyMPI en vez de CPython, a travs de mpirun. Los clusters ya tienen esto, pero es difcil de configurar de entrecasa
Hbridos
Pero son slo buffers, no pueden contener objetos Lo que es bueno, porque el GIL existe para proteger operaciones thread-unsafe en objetos
Pero son slo buffers, no pueden contener objetos Lo que es bueno, porque el GIL existe para proteger operaciones thread-unsafe en objetos
Resistir la tentacin de usar pickle, struct, y marshal
Eliminara la ventaja de mmap Usar pyrex en cambio, y su habilidad de castear punteros y trabajar directamente en el buffer
mmap permite
Lockless message queues Buffers compartidos Lookup tables compartidos Proxy Pool Cualquier otra estructura que se pueda trabajar directamente sobre el buffer
mmap permite
cdef class LocklessQueue: cdef object buf cdef void* vbuf cdef Py_ssize_t vbuflen cdef int _readp(self): return (<int*>self.vbuf)[0] cdef int _writep(self): return (<int*>self.vbuf)[1] def __init__(self, int capacity): self.buf = mmap(-1, sizeof(Msg)*capacity + sizeof(int)*2) PyObject_AsWriteBuffer(buf, &vbuf, &vbuflen)
mmap permite
cdef class LocklessQueue: ... def get(self): return MessageProxy(buf,self._readp()) def put(self): cdef object rv rv = MessageProxy(buf,self._writep()) (<int*>self.vbuf)[1] += sizeof(Msg) return rv def advance(self): (<int*>self.vbuf)[0] += sizeof(Msg)
mmap permite
Incluso se puede, con cuidado y pyrex, hacer versiones con ms de un productor o ms de un consumidor
mmap permite
Buffers compartidos
from multiprocessing import Process from mmap import mmap buf = mmap(-1,1000000) from random import random def randomize(buf,start,end): for i in xrange(start,end): buf[i] = chr(random()*255) map(Process.start, [Process(target=randomize, args=(buf,i*100000,(i+1)*100000)) for i in xrange(len(buf)/100000)])
mmap permite
Buffers compartidos
En linux multiprocessing no tiene tanto overhead, este uso es anlogo a OpenMP y multiprocessing lo implementa bien. Cuidado con archivos y sockets
mmap permite
Buffers compartidos
Ej: SharedPixelBuffer, un buffer de pixels compartidos donde todos pueden trabajar concurrentemente Ej: SharedIndex, un ndice comprimido que todos pueden consultar concurrentemente
mmap permite
Proxy Pool
Un objeto que provee un acceso directo a otro dentro de un buffer compartido Puede encapsular un mutex si fuera necesario Ej: MessageProxy en LocklessQueue
S, pero...
Ms sencillo, directo, y viene como mdulo estndar Ms portable, SHM slo tiene buen soporte en sistemas POSIX considerablemente recientes (linux >= 2.6) http://semanchuk.com/philip/posix_ipc/ Mdulo de python que permite acceso a IPC Posix
posix_ipc
SHM / Semforos / Mutex / Colas de mensajes SHM se usa con MMAP! (facilita la transicin a SHM si fuera necesaria)
Conclusin
Ningn mtodo es la panacea universal Cada problema tiene su truco Cada truco su punto fuerte
Y su punto dbil Cython tambin Recordar que existe por una razn
embrace pyrex