thread - Python-multiprocesamiento para matplotlib griddata
thread vs process python (1)
Ejecuté el código de ejemplo a continuación en Python 3.4.2, con la versión numpy 1.9.1 y matplotlib versión 1.4.2, en una Macbook Pro con 4 CPU físicas (es decir, a diferencia de las CPU "virtuales", que también tienen la arquitectura del hardware Mac pone a disposición para algunos casos de uso):
import numpy as np
import matplotlib.mlab as mlab
import time
import multiprocessing
# This value should be set much larger than nprocs, defined later below
size = 500
Y = np.arange(size)
X = np.arange(size)
x, y = np.meshgrid(X, Y)
u = x * np.sin(5) + y * np.cos(5)
v = x * np.cos(5) + y * np.sin(5)
test = x + y
tic = time.clock()
test_d = mlab.griddata(
x.flatten(), y.flatten(), test.flatten(), x+u, y+v, interp=''linear'')
toc = time.clock()
print(''Single Processor Time={0}''.format(toc-tic))
# Put interpolation points into a single array so that we can slice it easily
xi = x + u
yi = y + v
# My example test machine has 4 physical CPUs
nprocs = 4
jump = int(size/nprocs)
# Enclose the griddata function in a wrapper which will communicate its
# output result back to the calling process via a Queue
def wrapper(x, y, z, xi, yi, q):
test_w = mlab.griddata(x, y, z, xi, yi, interp=''linear'')
q.put(test_w)
# Measure the elapsed time for multiprocessing separately
ticm = time.clock()
queue, process = [], []
for n in range(nprocs):
queue.append(multiprocessing.Queue())
# Handle the possibility that size is not evenly divisible by nprocs
if n == (nprocs-1):
finalidx = size
else:
finalidx = (n + 1) * jump
# Define the arguments, dividing the interpolation variables into
# nprocs roughly evenly sized slices
argtuple = (x.flatten(), y.flatten(), test.flatten(),
xi[:,(n*jump):finalidx], yi[:,(n*jump):finalidx], queue[-1])
# Create the processes, and launch them
process.append(multiprocessing.Process(target=wrapper, args=argtuple))
process[-1].start()
# Initialize an array to hold the return value, and make sure that it is
# null-valued but of the appropriate size
test_m = np.asarray([[] for s in range(size)])
# Read the individual results back from the queues and concatenate them
# into the return array
for q, p in zip(queue, process):
test_m = np.concatenate((test_m, q.get()), axis=1)
p.join()
tocm = time.clock()
print(''Multiprocessing Time={0}''.format(tocm-ticm))
# Check that the result of both methods is actually the same; should raise
# an AssertionError exception if assertion is not True
assert np.all(test_d == test_m)
y obtuve el siguiente resultado:
/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/matplotlib/tri/triangulation.py:110: FutureWarning: comparison to `None` will result in an elementwise object comparison in the future.self._neighbors)
Single Processor Time=8.495998
Multiprocessing Time=2.249938
No estoy realmente seguro de qué está causando la "advertencia de futuro" de triangulation.py (evidentemente, a mi versión de matplotlib no le gustó nada acerca de los valores de entrada que se proporcionaron originalmente para la pregunta), pero independientemente de eso, el multiprocesamiento parece que se logra la aceleración deseada de 8.50 / 2.25 = 3.8 , ( edición: ver comentarios ) que está aproximadamente en el entorno de aproximadamente 4X que esperaríamos para una máquina con 4 CPU. Y la declaración de afirmación al final también se ejecuta con éxito, demostrando que los dos métodos obtienen la misma respuesta, así que a pesar del mensaje de advertencia ligeramente extraño, creo que el código anterior es una solución válida.
EDITAR: Un comentarista ha señalado que tanto mi solución como el fragmento de código publicado por el autor original probablemente utilizan el método incorrecto, time.clock()
, para medir el tiempo de ejecución; él sugiere usar time.time()
lugar. Creo que también estoy llegando a su punto de vista. (Profundizando un poco más en la documentación de Python, todavía no estoy convencido de que incluso esta solución sea 100% correcta, ya que las versiones más recientes de Python parecen tener obsoleto time.clock()
en favor de time.perf_counter()
y time.process_time()
. Pero independientemente de eso, estoy de acuerdo en que si o no time.time()
es la forma más correcta de tomar esta medida, es probablemente más correcto que lo que había estado usando antes, time.clock()
.)
Suponiendo que el punto del comentarista es correcto, entonces significa que la aceleración de aproximadamente 4X que pensé que había medido es, de hecho, incorrecta.
Sin embargo, eso no significa que el código subyacente en sí no esté correctamente paralelizado; más bien, simplemente significa que la paralelización en realidad no ayudó en este caso; La división de los datos y la ejecución en múltiples procesadores no mejoraron nada. ¿Por qué sería esto? Otros usuarios han señalado que, al menos en números y otras funciones, algunas funciones se ejecutan en varios núcleos y otras no, y puede ser un proyecto de investigación seriamente desafiante para un usuario final tratar de averiguar cuáles son cuáles.
Basado en los resultados de este experimento, si mi solución logra correctamente la paralelización dentro de Python, pero no se observa una mayor aceleración, entonces sugeriría que la explicación más sencilla es que matplotlib probablemente también paralice algunas de sus funciones "debajo del capó", por lo que Para hablar, en las bibliotecas compiladas de C ++, como ya lo hacen numpy / scipy. Suponiendo que ese sea el caso, entonces la respuesta correcta a esta pregunta sería que no se puede hacer nada más: una paralelización adicional en Python no servirá de nada si las bibliotecas de C ++ subyacentes ya se están ejecutando silenciosamente en varios núcleos, para empezar.
Siguiendo mi pregunta anterior [1] , me gustaría aplicar el multiprocesamiento a la función griddata de griddata
. ¿Es posible dividir los datos de cuadrícula en, digamos 4 partes, una para cada uno de mis 4 núcleos? Necesito esto para mejorar el rendimiento.
Por ejemplo, pruebe el siguiente código, experimentando con diferentes valores para el size
:
import numpy as np
import matplotlib.mlab as mlab
import time
size = 500
Y = np.arange(size)
X = np.arange(size)
x, y = np.meshgrid(X, Y)
u = x * np.sin(5) + y * np.cos(5)
v = x * np.cos(5) + y * np.sin(5)
test = x + y
tic = time.clock()
test_d = mlab.griddata(
x.flatten(), y.flatten(), test.flatten(), x+u, y+v, interp=''linear'')
toc = time.clock()
print ''Time='', toc-tic