2015-04-25 26 views
17

In seguito alla mia precedente domanda [1], vorrei applicare la multiprocessing alla funzione griddata di matplotlib. È possibile dividere la griddata in, diciamo 4 parti, una per ciascuno dei miei 4 core? Ho bisogno di questo per migliorare le prestazioni.Python - multiprocessing per matplotlib griddata

Ad esempio, provare il codice qui sotto, a sperimentare con valori diversi per 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 
+4

Non penso che sia possibile applicare il multiprocessing. forse, questa domanda http://stackoverflow.com/q/7424777/566035 è utile? – otterb

+0

Il codice di esempio non è sintatticamente corretto. Cosa intendevi fare con la seguente riga: 'test = xx + yy' – OYRM

+1

Ho corretto il codice, dovrebbe essere eseguito ora. –

risposta

5

Ho eseguito il codice di esempio riportato di seguito in Python 3.4.2, con la versione 1.9.1 e matplotlib numpy versione 1.4.2 , su un MacBook Pro con 4 CPU fisiche (cioè, al contrario di CPU "virtuali", che l'architettura hardware Mac mette a disposizione anche per alcuni casi di utilizzo):

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) 

ed ho ottenuto il seguente risultato:

Non sono sicuro di cosa stia causando il "warning futuro" da triangulation.py (evidentemente la mia versione di matplotlib non ha gradito qualcosa sui valori di input originariamente forniti per la domanda), ma a prescindere, il multiprocessing sembra raggiungere la velocità desiderata di 8.50/2.25 = 3.8, (modifica: vedere i commenti) che si trova approssimativamente attorno a circa 4X che ci aspetteremmo per una macchina con 4 CPU. Anche la dichiarazione di asserzione alla fine viene eseguita correttamente, dimostrando che i due metodi ottengono la stessa risposta, quindi nonostante il messaggio di avviso un po 'strano, credo che il codice sopra sia una soluzione valida.


EDIT: Un commentatore ha sottolineato che sia la mia soluzione, così come il frammento di codice scritto dall'autore originale, sono probabilmente utilizzando il metodo sbagliato, time.clock(), per misurare il tempo di esecuzione; suggerisce invece di usare time.time(). Penso che mi stia avvicinando anche al suo punto di vista. (Scavando ulteriormente nella documentazione Python, non sono ancora convinto che anche questa soluzione sia corretta al 100%, poiché le versioni più recenti di Python sembrano deprecate time.clock() in favore di time.perf_counter() e time.process_time(). Ma a prescindere, sono d'accordo che o no time.time() è assolutamente il modo più corretto di prendere questa misura, è ancora probabilmente più corretto di quello che avevo usato prima, time.clock().)

Assumendo il punto del commentatore è corretta, allora significa che il 4X aumento di velocità di circa che ho il pensiero che avevo misurato è in realtà sbagliato.

Tuttavia, ciò non significa che il codice sottostante non fosse correttamente parallelizzato; piuttosto, significa solo che la parallelizzazione in realtà non ha aiutato in questo caso; la suddivisione dei dati e l'esecuzione su più processori non ha migliorato nulla. Perché dovrebbe essere? Altri utenti hanno pointed out che, almeno in numpy/scipy, alcune funzioni funzionano su più core, e altri no, e può essere un progetto di ricerca seriamente impegnativo per un utente finale per cercare di capire quali sono quali.

Sulla base dei risultati di questo esperimento, se la mia soluzione ottiene correttamente la parallelizzazione in Python, ma non si osserva alcuna ulteriore accelerazione, quindi suggerirei la spiegazione più semplice probabile che matplotlib stia probabilmente parallelizzando alcune delle sue funzioni "sotto il cappuccio ", per così dire, in librerie C++ compilate, proprio come già fanno numpy/scipy. Supponendo che sia così, allora la risposta corretta a questa domanda sarebbe che non si può fare altro: parallelizzare ulteriormente in Python non servirà a niente se le librerie C++ sottostanti sono già in esecuzione silenziosa su più core per cominciare.

+1

Sfortunatamente non state calcolando l'ora di wall clock usando '' time.clock() '' (consultate http://stackoverflow.com/a/23325328/1510289). Invece, usa '' time.time() '' e nota che lo scenario di multiprocessing richiede più tempo. È un bel tentativo, però! Anch'io ho provato a suddividere i valori di input e non ho trovato alcuna accelerazione a '' griddata() '' di sorta. :( –

+1

Scusa ma la risposta di @stachyra non è corretta Sostituendo '' time.clock() '' con '' time.time() '' le prestazioni del wall clock sono peggiori La mia macchina a 8 CPU fornisce: '' Single Processor Tempo = 8.833 Tempo di multiprocessing = 11.677'' –

+0

Non posso lanciarlo ... ho ricevuto un errore: "Traceback (ultima chiamata ultima): File" /usr/lib/python2.7/multiprocessing/process.py ", riga 258, in _bootstrap self._target (* self._args, ** self._kwargs) File "", riga 11, nel wrapper test_w = mlab.griddata (x, y , z, xi, yi, interp = 'linear') File "/usr/lib/pymodules/python2.7/matplotlib/mlab.py", riga 2619, in griddata raise ValueError ("la griglia di output deve avere spaziatura costante " ValueError: la griglia di output deve avere costante sp acing quando si utilizza interp = 'linear' ... " – user3601754