2015-07-09 5 views
7

Ho una macchina con 24 core fisici (almeno mi è stato detto così) che eseguono Debian: Linux 3.2.0-4-amd64 #1 SMP Debian 3.2.68-1+deb7u1 x86_64 GNU/Linux. Essa sembra essere corretta:Python: multiprocessing, 8/24 core caricati

[email protected]:~/$ cat /proc/cpuinfo | grep processor 
processor : 0 
processor : 1 
<...> 
processor : 22 
processor : 23 

Ho avuto alcuni problemi che tentano di caricare tutti i core con Python di multiprocessing.pool.Pool. Ho usato Pool(processes=None); i documenti dicono che Python usa cpu_count() se viene fornito None.

Ahimè, solo 8 core sono stati caricati al 100%, altri sono rimasti inattivi (ho utilizzato htop per monitorare il carico della CPU). Ho pensato che non posso cucinare Pools correttamente e ho cercato di richiamare 24 processi "manualmente":

print 'Starting processes...' 
procs = list() 
for param_set in all_params: # 24 items 
    p = Process(target=_wrap_test, args=[param_set]) 
    p.start() 
    procs.append(p) 

print 'Now waiting for them.' 
for p in procs: 
    p.join() 

avevo 24 messaggi "auguri" dai processi ho iniziato:

Starting processes... 
Executing combination: Session len: 15, delta: 10, ratio: 0.1, eps_relabel: 0.5, min_pts_lof: 5, alpha: 0.01, reduce: 500 
< ... 22 more messages ... > 
Executing combination: Session len: 15, delta: 10, ratio: 0.1, eps_relabel: 0.5, min_pts_lof: 7, alpha: 0.01, reduce: 2000 
Now waiting for them. 

Ma ancora solo 8 nuclei sono stati caricati:

enter image description here

ho letto qui sul SO che ci m possono essere problemi con numpy, OpenBLAS e l'esecuzione multicore. Questo è il modo di iniziare il mio codice:

OPENBLAS_MAIN_FREE=1 python -m tests.my_module 

E dopo tutte le importazioni che faccio:

os.system("taskset -p 0xff %d" % os.getpid()) 

Quindi, ecco la domanda: che cosa devo fare per avere il 100% -load su tutti i core? Questo è solo il mio scarso utilizzo di Python o ha qualcosa a che fare con le limitazioni del sistema operativo su macchine multicore?

AGGIORNATO: una cosa più interessante è qualche incongruenza nell'output htop. Se guardi l'immagine sopra, vedrai che la tabella sotto le barre di caricamento della CPU mostra il 30-50% di carico per molto più di 8 core, che è decisamente diverso da quello che dicono le barre di caricamento. Quindi, top sembra essere d'accordo con quelle barre: 8 core caricati al 100%, altri inattivi.

AGGIORNATO ANCORA UNA VOLTA:

Ho usato this rather popular post su SO quando ho aggiunto la linea os.system("taskset -p 0xff %d" % os.getpid()) dopo tutte le importazioni. Devo ammettere che non ho pensato troppo quando l'ho fatto, soprattutto dopo aver letto questo:

Con questa formazione incollato in dopo le importazioni di moduli, il mio esempio ora gira su tutti i core

Sono un uomo semplice. Vedo "funziona come un incantesimo", copio e incollo. Comunque, mentre giocavo con il mio codice, alla fine ho rimosso . Successivamente il mio codice ha iniziato l'esecuzione su tutti i 24 core per lo scenario di avvio "manuale" Process. Per lo scenario Pool, lo stesso problema rimaneva, indipendentemente dal fatto che il trucco di affinità fosse usato o meno.

Non penso che sia una risposta reale perché non so quale sia il problema con Pool, ma almeno sono riuscito a caricare tutti i core completamente. Grazie!

+0

Sei sicuro che si tratta di 1 scheda processore? Ho sentito dire che Python non può fare multiprocessing (usare più di 1 CPU) – deathangel908

+0

@ deathangel908 Suppongo che abbia 4 CPU con 6 core ciascuno. Ma utilizza già più di 6 core, quindi non è il problema credo. – oopcode

+0

@ deathangel908 che è errato: ci sono problemi nel ricevere il threading per usare tutte le risorse della macchina, ma il multiprocessing, usando processi unix separati, non è limitato da Python. La mia ipotesi è che ci sia qualche impostazione del kernel che non sia impostata correttamente come l'OP ha indovinato. – msw

risposta

2

Anche se hai risolto il problema, cercherò di spiegarlo per chiarire le idee.

Per quello che leggo in giro, Numpy fa un sacco di "magia" per migliorare le prestazioni. Uno dei trucchi magici è impostare l'affinità della CPU del processo.

L'affinità della CPU è un'ottimizzazione dello scheduler del sistema operativo. In pratica, applica un dato processo per essere sempre eseguito sullo stesso core della CPU.

Questo migliora le prestazioni riducendo la quantità di volte in cui la cache della CPU viene invalidata e aumentando i vantaggi della località di riferimento. Per gli alti compiti computazionali questi fattori sono davvero importanti.

Quello che non mi piace di Numpy è il fatto che fa tutto questo implicitamente. Spesso sviluppatori sconcertanti.

Il fatto che i processi in cui non è in esecuzione su tutti i core era dovuto al fatto che numpy imposta l'affinità al processo padre quando si importa il modulo. Quindi, quando si generano i nuovi processi, l'affinità viene ereditata portando a tutti i processi che combattono per pochi nuclei invece di utilizzare in modo efficiente tutti quelli disponibili.

Il comando os.system("taskset -p 0xff %d" % os.getpid()) istruisce il sistema operativo per impostare l'affinità su tutti i core che risolvono il problema.

Se vuoi vederlo funzionare sul Pool puoi fare il seguente trucco.

import os 
from multiprocessing import Pool 


def set_affinity_on_worker(): 
    """When a new worker process is created, the affinity is set to all CPUs""" 
    print("I'm the process %d, setting affinity to all CPUs." % os.getpid()) 
    os.system("taskset -p 0xff %d" % os.getpid()) 


if __name__ == '__main__': 
    p = Pool(initializer=set_affinity_on_worker) 
    ...