threads - La aplicación paralela en Python se vuelve mucho más lenta cuando se usa mpi en lugar de un módulo de multiprocesamiento
python procesamiento paralelo (1)
MPI en realidad está diseñado para hacer comunicación entre nodos, por lo tanto, hable con otras máquinas a través de la red. El uso de MPI en el mismo nodo puede resultar en una gran sobrecarga para cada mensaje que debe enviarse, en comparación con, por ejemplo, el subprocesamiento.
mpi4py hace una copia para cada mensaje, ya que está dirigido al uso de memoria distribuida. Si su OpenMPI no está configurado para usar la memoria compartida para la comunicación entre nodos, este mensaje se enviará a través de la pila de TCP del kernel y se enviará al otro proceso, lo que volverá a agregar cierta sobrecarga.
Si solo pretende realizar cálculos dentro de la misma máquina, no es necesario utilizar mpi aquí.
Algo de esto se discute en este hilo.
Actualización El proyecto ipc-benchmark intenta dar una idea de cómo funcionan los diferentes tipos de comunicación en diferentes sistemas. (multinúcleo, multiprocesador, memoria compartida) ¡Y especialmente cómo esto influye en las máquinas virtualizadas!
Recomiendo ejecutar el ipc-benchmark en la máquina virtualizada y publicar los resultados. Si se parecen en algo a this punto de referencia, puede brindarle una gran perspectiva de la diferencia entre TCP, sockets y tuberías.
Últimamente he observado un efecto extraño cuando medí el rendimiento de mi aplicación paralela utilizando el módulo de multiprocesamiento y mpi4py como herramientas de comunicación.
La aplicación realiza algoritmos evolutivos en conjuntos de datos. La mayoría de las operaciones se realizan secuencialmente con la excepción de la evaluación. Después de que se aplican todos los operadores evolutivos, todos los individuos necesitan recibir nuevos valores de aptitud física, lo cual se realiza durante la evaluación. Básicamente es solo un cálculo matemático realizado en una lista de flotadores (python ones). Antes de la evaluación, un conjunto de datos se dispersa por la dispersión del mpi o el Pool.map de python, luego viene la evaluación paralela y luego los datos regresan a través de la recopilación del mpi o nuevamente el mecanismo de Pool.map.
Mi plataforma de referencia es una máquina virtual (virtualbox) que ejecuta Ubuntu 11.10 con Open MPI 1.4.3 en Core i7 (4/8 cores), 8 GB de RAM y una unidad SSD.
Lo que encuentro realmente sorprendente es que adquiero una buena aceleración; sin embargo, dependiendo de una herramienta de comunicación, después de un cierto umbral de procesos, el rendimiento empeora. Se puede ilustrar con las siguientes imágenes.
eje y - tiempo de procesamiento
eje x - nr de procesos
colores - tamaño de cada individuo (nr de flotadores)
1) Usando módulo de multiprocesamiento - Pool.map
2) Usando mpi - Dispersión / Recolección
3) Ambas imágenes una encima de la otra
Al principio pensé que es culpa de Hyperthreading, porque para grandes conjuntos de datos se vuelve más lento después de alcanzar 4 procesos (4 núcleos físicos). Sin embargo, también debería ser visible en el caso de multiprocesamiento y no lo es. Mi otra conjetura es que los métodos de comunicación mpi son mucho menos efectivos que los de python, sin embargo, me resulta difícil de creer.
¿Alguien tiene alguna explicación para estos resultados?
ADICIONAL:
Estoy empezando a creer que es culpa de Hyperthreading después de todo. Probé mi código en una máquina con Core i5 (2/4 cores) y el rendimiento es peor con 3 o más procesos. La única explicación que me viene a la mente es que el i7 que estoy usando no tiene suficientes recursos (¿caché?) Para calcular la evaluación simultáneamente con Hyperthreading y necesita programar más de 4 procesos para ejecutarse en 4 núcleos físicos.
Sin embargo, lo interesante es que cuando uso mpi htop muestra la utilización completa de los 8 núcleos lógicos, lo que debería sugerir que la declaración anterior es incorrecta. Por otro lado, cuando uso Pool.Map, no utiliza completamente todos los núcleos. Utiliza uno o dos al máximo y el resto solo parcialmente, de nuevo, ni idea de por qué se comporta de esta manera. Mañana adjuntaré una captura de pantalla que muestra este comportamiento.
No estoy haciendo nada sofisticado con el código, es muy sencillo (no estoy dando todo el código, no porque sea secreto, sino porque necesita que se instalen bibliotecas adicionales como DEAP. Si alguien está realmente interesado en el problema y está listo Para instalar DEAP puedo preparar un breve ejemplo). El código para MPI es un poco diferente, porque no puede tratar con un contenedor de población (que se hereda de la lista). Hay algunos gastos generales, por supuesto, pero nada importante. Aparte del código que muestro a continuación, el resto es el mismo.
Pool.map:
def eval_population(func, pop):
for ind in pop:
ind.fitness.values = func(ind)
return pop
# ...
self.pool = Pool(8)
# ...
for iter_ in xrange(nr_of_generations):
# ...
self.pool.map(evaluate, pop) # evaluate is really an eval_population alias with a certain function assigned to its first argument.
# ...
MPI - Dispersión / Recolección
def divide_list(lst, n):
return [lst[i::n] for i in xrange(n)]
def chain_list(lst):
return list(chain.from_iterable(lst))
def evaluate_individuals_in_groups(func, rank, individuals):
comm = MPI.COMM_WORLD
size = MPI.COMM_WORLD.Get_size()
packages = None
if not rank:
packages = divide_list(individuals, size)
ind_for_eval = comm.scatter(packages)
eval_population(func, ind_for_eval)
pop_with_fit = comm.gather(ind_for_eval)
if not rank:
pop_with_fit = chain_list(pop_with_fit)
for index, elem in enumerate(pop_with_fit):
individuals[index] = elem
for iter_ in xrange(nr_of_generations):
# ...
evaluate_individuals_in_groups(self.func, self.rank, pop)
# ...
AGREGADO 2: Como mencioné anteriormente, hice algunas pruebas en mi máquina i5 (2/4 núcleos) y aquí está el resultado:
También encontré una máquina con 2 xeons (2x 6/12 cores) y repetí el punto de referencia:
Ahora tengo 3 ejemplos del mismo comportamiento. Cuando ejecuto mi computación en más procesos que núcleos físicos, comienza a empeorar. Creo que es porque los procesos en el mismo núcleo físico no se pueden ejecutar simultáneamente debido a la falta de recursos.