2013-07-02 2 views
7

Sulla base di questo question ho ipotizzato che la creazione di nuovo processo dovrebbe essere quasi veloce come la creazione dinuovo thread in Linux. Tuttavia, un piccolo test ha mostrato risultati molto diversi. Ecco il mio codice:Python vs. threading multiprocessing in Linux

from multiprocessing import Process, Pool 
from threading import Thread 

times = 1000 

def inc(a): 
    b = 1 
    return a + b 

def processes(): 
    for i in xrange(times): 
     p = Process(target=inc, args=(i,)) 
     p.start() 
     p.join() 

def threads(): 
    for i in xrange(times): 
     t = Thread(target=inc, args=(i,)) 
     t.start() 
     t.join() 

Test:

>>> timeit processes() 
1 loops, best of 3: 3.8 s per loop 

>>> timeit threads() 
10 loops, best of 3: 98.6 ms per loop 

Così, i processi sono quasi 40 volte più lento per creare! Perché succede? E 'specifico per Python o queste librerie? O ho semplicemente interpretato male la risposta sopra?


UPD 1. Per renderlo più chiaro. Comprendo che questa parte di codice non introduce alcuna concorrenza. L'obiettivo qui è di testare il tempo necessario per creare un processo e un thread. Per utilizzare vera e propria concorrenza con Python si può usare qualcosa di simile a questo:

def pools(): 
    pool = Pool(10) 
    pool.map(inc, xrange(times)) 

che corre in realtà molto più veloce rispetto alla versione filettata.


UPD 2. Ho aggiunto la versione con os.fork():

for i in xrange(times): 
    child_pid = os.fork() 
    if child_pid: 
     os.waitpid(child_pid, 0) 
    else: 
     exit(-1) 

I risultati sono:

$ time python test_fork.py 

real 0m3.919s 
user 0m0.040s 
sys  0m0.208s 

$ time python test_multiprocessing.py 

real 0m1.088s 
user 0m0.128s 
sys  0m0.292s 

$ time python test_threadings.py 

real 0m0.134s 
user 0m0.112s 
sys  0m0.048s 
+0

Bene, la domanda a cui si è collegati sta confrontando il costo della semplice chiamata di 'fork (2)' contro 'pthread_create (3)', mentre il codice fa un po 'di più. Che ne dici di confrontare 'os.fork()' con 'thread.start_new_thread()'? – Aya

+0

@Aya: Non sono riuscito a trovare alcun tipo di 'join' nel modulo' thread' per creare test simili, ma anche rispetto alla versione 'threading' di alto livello con' os.fork() 'è ancora molto più lento. In realtà, è il più lento (anche se condizioni aggiuntive possono influire sulle prestazioni). Vedi il mio aggiornamento. – ffriend

+0

Devi usare un mutex per aspettare il thread se stai usando il modulo 'thread' di basso livello, che è come il modulo' threading' di livello superiore implementa 'join()'. Ma, se stai solo provando a misurare il tempo necessario per creare il nuovo processo/thread, non dovresti chiamare 'join()'. Vedi anche la mia risposta qui sotto. – Aya

risposta

5

La domanda a cui si è collegati sta confrontando il costo della semplice chiamata allo fork(2) rispetto allo pthread_create(3), mentre il codice fa un bel po 'di più, ad es. utilizzando join() per attendere che i processi/thread terminino.

Se, come dici tu ...

L'obiettivo qui è quello di testare il tempo necessario per creare un processo e un filo.

... quindi non dovresti aspettare che vengano completati. Si deve usare i programmi di test più come questi ...

fork.py

import os 
import time 

def main(): 
    for i in range(100): 
     pid = os.fork() 
     if pid: 
      #print 'created new process %d' % pid 
      continue 
     else: 
      time.sleep(1) 
      return 

if __name__ == '__main__': 
    main() 

thread.py

import thread 
import time 

def dummy(): 
    time.sleep(1) 

def main(): 
    for i in range(100): 
     tid = thread.start_new_thread(dummy,()) 
     #print 'created new thread %d' % tid 

if __name__ == '__main__': 
    main() 

... che danno i seguenti risultati ..

$ time python fork.py 
real 0m0.035s 
user 0m0.008s 
sys  0m0.024s 

$ time python thread.py 
real 0m0.032s 
user 0m0.012s 
sys  0m0.024s 

... s o non c'è molta differenza nel tempo di creazione di thread e processi.

+0

Ma il tuo fork.py non creerà solo nuovi thread ed uscirà, senza aspettare che i processi figli vengano completati? – ffriend

+0

Inoltre, si avvia il thread/processo successivo senza attendere il completamento della precedente, in modo che vengano eseguiti contemporaneamente, mentre sembra essere più corretto avviarli in sequenza per evitare GIL e tutte queste cose. – ffriend

+0

@ffriend Beh, la tua domanda ha detto (il mio enfasi) "Ho pensato che ** creare ** un nuovo processo dovrebbe essere quasi veloce quanto creare un nuovo thread in Linux", ed è così. L'intero punto di utilizzo dei thread è per la concorrenza, quindi quale sarebbe il punto di esecuzione dei thread in sequenza? Che cosa stai cercando di ottenere esattamente qui? – Aya

2

Sì, è vero. Iniziare un nuovo processo (chiamato processo pesante) è costoso.

Come una panoramica ...

Il sistema operativo deve (nel caso di Linux) Forcella il primo processo, impostare la contabilità per il nuovo processo, impostare il nuovo stack, fare il cambio di contesto, copia qualsiasi memoria che viene cambiata e distruggere tutto quando il nuovo processo ritorna.

Il thread assegna appena un nuovo stack e struttura del thread, esegue il contesto switch e restituisce quando il lavoro è terminato.

... è per questo che utilizziamo i thread.

+0

hai fatto all'indietro. un processo è solo un processo. un thread è un processo leggero :) Immagino che tu possa chiamare un processo un thread pesante, ma non penso che nessuno lo faccia. cos'è un processo pesante? – thang

+0

@thang Sigh. Se non sai sth, almeno potresti farlo su Google. Prova a cercare su Google "il processo dei pesi massimi" e vedi se * qualcuno lo fa *. – andy256

1

Nella mia esperienza c'è una differenza significativa tra la creazione di un thread (con pthread_create) e la forking di un processo.

Per esempio ho creato un test C simile al vostro test di pitone con il codice di filo in questo modo:

pthread_t thread; 
pthread_create(&thread, NULL, &test, NULL); 
void *res; 
pthread_join(thread, &res); 

e di processo codice forking in questo modo:

pid_t pid = fork(); 
if (!pid) { 
    test(NULL); 
    exit(0); 
}   
int res; 
waitpid(pid, &res, 0); 

Sul mio sistema il codice forking preso circa 8 volte più lungo da eseguire.

Tuttavia, vale la pena notare che l'implementazione di Python è ancora più lenta - per me è stata circa 16 volte più lenta.Sospetto che sia perché oltre al normale sovraccarico di creazione di un nuovo processo, c'è anche più overhead di Python associato al nuovo processo.