2014-05-08 9 views
5

Sto incontrando un bizzarro problema che non riesco a spiegare. Spero che qualcuno là fuori possa aiutare per favore!Importazione del supporto per il multiprocessing in Scipy break in Python

Io corro Python 2.7.3 e SciPy v0.14.0 e sto cercando di attuare alcune molto semplici algoritmi multiprocessore a velocità fino mio codice utilizzando il modulo multiprocessing. Sono riuscito a far funzionare un esempio di base:

import multiprocessing 
import numpy as np 
import time 
# import scipy.special 


def compute_something(t): 
    a = 0. 
    for i in range(100000): 
     a = np.sqrt(t) 
    return a 

if __name__ == '__main__': 

    pool_size = multiprocessing.cpu_count() 
    print "Pool size:", pool_size 
    pool = multiprocessing.Pool(processes=pool_size) 

    inputs = range(10) 

    tic = time.time() 
    builtin_outputs = map(compute_something, inputs) 
    print 'Built-in:', time.time() - tic 

    tic = time.time() 
    pool_outputs = pool.map(compute_something, inputs) 
    print 'Pool :', time.time() - tic 

Questo funziona benissimo, tornando

Pool size: 8 
Built-in: 1.56904006004 
Pool : 0.447728157043 

Ma se togliere il commento alla linea di import scipy.special, ottengo:

Pool size: 8 
Built-in: 1.58968091011 
Pool : 1.59387993813 

ed io posso vedere che solo un core sta facendo il lavoro sul mio sistema. In effetti, l'importazione di qualsiasi modulo dal pacchetto scipy sembra avere questo effetto (ne ho provati diversi).

Qualche idea? Non ho mai visto un caso come questo prima, dove un'importazione apparentemente innocua può avere un effetto così strano e inaspettato.

Grazie!

aggiornamento (1)

Spostando la linea di importazione SciPy alla funzione compute_something migliora parzialmente il problema:

Pool size: 8 
Built-in: 1.66807389259 
Pool : 0.596321105957 

Update (2)

Grazie a @larsmans per test su un sistema diverso. Il problema non è stato confermato usando Scipy v.0.12.0. Spostando questa query nella mailing list di scipy e posterai qualsiasi risposta.

+0

Impossibile riprodurre con Python 2.7.5, SciPy 0.12.0. Che versione stai usando? –

+0

Interessante, grazie per aver provato! Sto usando 0.14.0b1. Ho bisogno di alcuni dei moduli più recenti, quindi utilizzando una versione più recente. – Gabriel

+0

Suggerisco anche di provare la versione stabile - e se questo risolve il problema, prova a contattare la mailing list di SciPy. Il debug di una versione beta di una libreria non è davvero roba del genere. –

risposta

7

Dopo aver scavato molto e posting an issue sul sito Scipy GitHub, ho trovato una soluzione.

Prima di iniziare, questo è documentato molto bene here - Mi limiterò a dare una panoramica.

Questo problema è non correlato alla versione di Scipy o Numpy che stavo usando. Ha origine nelle librerie BLAS di sistema che Numpy e Scipy utilizzano per varie routine di algebra lineare. Si può dire che le biblioteche Numpy è legata alla eseguendo

python -c 'import numpy; numpy.show_config()'

Se si utilizza OpenBLAS in Linux, è possibile che l'affinità della CPU è impostato a 1, il che significa che una volta che questi algoritmi sono importati in Python (via Numpy/Scipy), è possibile accedere al massimo un core della CPU. Per testare questa, di un terminale pitone corsa

import os 
os.system('taskset -p %s' %os.getpid()) 

Se l'affinità CPU viene restituito come f, di ff, è possibile accedere a più nuclei. Nel mio caso sarebbe iniziato così, ma dopo aver importato numpy o scipy.any_module, passerebbe a 1, quindi il mio problema.

ho trovato due soluzioni:

affinità Change CPU

È possibile impostare manualmente l'affinità CPU del processo principale nella parte superiore della funzione principale in modo che il codice simile a questo:

import multiprocessing 
import numpy as np 
import math 
import time 
import os 

def compute_something(t): 
    a = 0. 
    for i in range(10000000): 
     a = math.sqrt(t) 
    return a 

if __name__ == '__main__': 

    pool_size = multiprocessing.cpu_count() 
    os.system('taskset -cp 0-%d %s' % (pool_size, os.getpid())) 

    print "Pool size:", pool_size 
    pool = multiprocessing.Pool(processes=pool_size) 

    inputs = range(10) 

    tic = time.time() 
    builtin_outputs = map(compute_something, inputs) 
    print 'Built-in:', time.time() - tic 

    tic = time.time() 
    pool_outputs = pool.map(compute_something, inputs) 
    print 'Pool :', time.time() - tic 

si noti che la selezione di un valore superiore al numero di core per taskset non sembra avere importanza - si utilizza solo il numero massimo possibile.

interruttore BLAS librerie

Soluzione documentato al site linkato sopra. Fondamentalmente: installare libatlas ed eseguire update-alternatives per puntare numpy su ATLAS piuttosto che su OpenBLAS.