2012-04-14 5 views
6

Ora sto studiando come recuperare i dati dal sito Web il più velocemente possibile. Per ottenere una maggiore velocità, sto considerando l'utilizzo di multi-thread. Ecco il codice che ho usato per testare la differenza tra post multi-thread e semplice.Come ottenere una maggiore velocità quando si utilizza il multi-threading in python

import threading 
import time 
import urllib 
import urllib2 


class Post: 

    def __init__(self, website, data, mode): 
     self.website = website 
     self.data = data 

     #mode is either "Simple"(Simple POST) or "Multiple"(Multi-thread POST) 
     self.mode = mode 

    def post(self): 

     #post data 
     req = urllib2.Request(self.website) 
     open_url = urllib2.urlopen(req, self.data) 

     if self.mode == "Multiple": 
      time.sleep(0.001) 

     #read HTMLData 
     HTMLData = open_url.read() 



     print "OK" 

if __name__ == "__main__": 

    current_post = Post("http://forum.xda-developers.com/login.php", "vb_login_username=test&vb_login_password&securitytoken=guest&do=login", \ 
         "Simple") 

    #save the time before post data 
    origin_time = time.time() 

    if(current_post.mode == "Multiple"): 

     #multithreading POST 

     for i in range(0, 10): 
      thread = threading.Thread(target = current_post.post) 
      thread.start() 
      thread.join() 

     #calculate the time interval 
     time_interval = time.time() - origin_time 

     print time_interval 

    if(current_post.mode == "Simple"): 

     #simple POST 

     for i in range(0, 10): 
      current_post.post() 

     #calculate the time interval 
     time_interval = time.time() - origin_time 

     print time_interval 

proprio come si può vedere, questo è un codice molto semplice. prima ho impostato la modalità su "Semplice", e posso ottenere l'intervallo di tempo: 50s (forse la mia velocità è un po 'lenta :(). poi ho impostato la modalità su "Multiple", e ottengo l'intervallo di tempo: . da quello che posso vedere, multi-thread può effettivamente aumentare la velocità, ma l'isnt risultato buono come immagino. Voglio ottenere una velocità molto più veloce.

dal debug, ho trovato che il programma principalmente blocchi alla riga: open_url = urllib2.urlopen(req, self.data), questa riga di codice impiega molto tempo per pubblicare e ricevere dati dal sito Web specificato, suppongo che sia possibile ottenere una maggiore velocità aggiungendo time.sleep() e utilizzando la funzione multi-threading all'interno della funzione urlopen, ma non posso farlo perché è la stessa funzione del pitone

se non considerando i limiti possibili che il server blocca la velocità del post, cos'altro posso fare per ottenere una maggiore velocità? o qualsiasi altro codice che posso modificare? grazie mille!

+1

threading è una cattiva idea in python, diventa un collo di bottiglia facilmente e possono rimanere intrappolati dalla GIL, provare multiprocessing. –

+1

@JakobBowyer: i thread sono dettagli di implementazione qui, il vero obiettivo è avere più connessioni aperte. L'aspetto GIL del threading in Python non ha alcun ruolo qui. – orlp

+0

@nightcracker, dovresti davvero leggere su GIL e fare il threading prima di fare affermazioni del genere ... inizia qui: [PyCon 2010: Capire il Python GIL] (http://python.mirocommunity.org/video/1479/pycon- 2010-understanding-the-p) –

risposta

5

In molti casi, la filettatura del pitone non migliora la velocità di esecuzione molto bene ... a volte, lo rende peggiore. Per ulteriori informazioni, vedere David Beazley's PyCon2010 presentation on the Global Interpreter Lock/Pycon2010 GIL slides.Questa presentazione è molto istruttiva, lo consiglio vivamente a chiunque stia valutando la filettatura ...

È necessario utilizzare lo multiprocessing module. Ho incluso questo come opzione nel tuo codice (vedi il fondo della mia risposta).

L'esecuzione di questo su una delle mie macchine più vecchie (Python 2.6.6):

current_post.mode == "Process" (multiprocessing) --> 0.2609 seconds 
current_post.mode == "Multiple" (threading)  --> 0.3947 seconds 
current_post.mode == "Simple" (serial execution) --> 1.650 seconds 

Sono d'accordo con il commento di TokenMacGuy ei numeri sopra indicati comprendono lo spostamento del .join() ad un ciclo diverso. Come puoi vedere, il multiprocessing di Python è molto più veloce del threading.


from multiprocessing import Process 
import threading 
import time 
import urllib 
import urllib2 


class Post: 

    def __init__(self, website, data, mode): 
     self.website = website 
     self.data = data 

     #mode is either "Simple"(Simple POST) or "Multiple"(Multi-thread POST) 
     self.mode = mode 

    def post(self): 

     #post data 
     req = urllib2.Request(self.website) 
     open_url = urllib2.urlopen(req, self.data) 

     if self.mode == "Multiple": 
      time.sleep(0.001) 

     #read HTMLData 
     HTMLData = open_url.read() 

     print "OK" 

if __name__ == "__main__": 

    current_post = Post("http://forum.xda-developers.com/login.php", "vb_login_username=test&vb_login_password&securitytoken=guest&do=login", \ 
         "Process") 
    #save the time before post data 
    origin_time = time.time() 

    if(current_post.mode == "Multiple"): 

     #multithreading POST 
     threads = list() 
     for i in range(0, 10): 
      thread = threading.Thread(target = current_post.post) 
      thread.start() 
      threads.append(thread) 
     for thread in threads: 
      thread.join() 
     #calculate the time interval 
     time_interval = time.time() - origin_time 
     print time_interval 

    if(current_post.mode == "Process"): 

     #multiprocessing POST 
     processes = list() 
     for i in range(0, 10): 
      process = Process(target=current_post.post) 
      process.start() 
      processes.append(process) 
     for process in processes: 
      process.join() 
     #calculate the time interval 
     time_interval = time.time() - origin_time 
     print time_interval 

    if(current_post.mode == "Simple"): 

     #simple POST 
     for i in range(0, 10): 
      current_post.post() 
     #calculate the time interval 
     time_interval = time.time() - origin_time 
     print time_interval 
+0

thx molto. il multiprocessing è una buona idea, è davvero un po 'più veloce del multi-threading sul mio computer. grazie a tutti voi. ho imparato molto dalla domanda. – Searene

+0

@MarkZar, direi che un miglioramento della velocità del 33% è più che un po 'più veloce, ma a prescindere ti auguro il tuo progetto. –

0

Una ricerca DNS richiede tempo. Non c'è nulla che tu possa fare al riguardo. Le latenze di grandi dimensioni sono un motivo per utilizzare più thread in primo luogo: il sito di annunci GET/POST di più ricerche può quindi verificarsi in parallelo.

Scarica il sonno() - non aiuta.

+0

Thx, ma ho solo confuso perché 'time.sleep()' è inutile. Effettivamente, funziona bene anche dopo aver scaricato 'sleep()', ma come può realizzare multi-thread senza 'sleep()'? Python esegue automaticamente thread diversi a intervalli casuali? in tal caso, qual è l'uso della funzione 'sleep()'? – Searene

+0

Non è inutile, semplicemente inappropriato qui. Uso del sonno - ci sono carichi. 'Dopo aver acceso la pompa, attendere almeno dieci secondi affinché la pressione si stabilizzi prima di aprire la valvola di alimentazione'. –

0

Ricordare che l'unico caso in cui il multi-threading può "aumentare la velocità" in Python è quando si hanno operazioni come questa che sono fortemente vincolate all'I/O. Altrimenti il ​​multi-threading non aumenta la "velocità" dal momento che non può essere eseguito su più di una CPU (no, nemmeno se si hanno più core, python non funziona in questo modo). È necessario utilizzare il multithreading quando si desidera eseguire due operazioni contemporaneamente, non quando si desidera che due elementi siano paralleli (ovvero due processi eseguiti separatamente).

Ora, ciò che stai facendo in realtà non aumenterà la velocità di ogni singola ricerca DNS, ma permetterà di sparare più richieste mentre aspetti i risultati di altri, ma dovresti stare attento a quanti ne fai o semplicemente rendi i tempi di risposta ancora peggiori di quelli che già sono.

Inoltre si prega di smettere di usare urllib2, e utilizzare le richieste: http://docs.python-requests.org

7

La più grande cosa si sta facendo male, che sta danneggiando il vostro rendimento di più, è il modo in cui si sta chiamando thread.start() e thread.join():

for i in range(0, 10): 
    thread = threading.Thread(target = current_post.post) 
    thread.start() 
    thread.join() 

Ogni volta che si attraversa il ciclo, si crea un thread, lo si avvia e quindi si attende che finisca Prima di passare al thread successivo. Non stai facendo nulla in modo concorrente!

Cosa si dovrebbe probabilmente essere facendo invece è:

threads = [] 

# start all of the threads 
for i in range(0, 10): 
    thread = threading.Thread(target = current_post.post) 
    thread.start() 
    threads.append(thread) 

# now wait for them all to finish 
for thread in threads: 
    thread.join() 
+0

Non ho nemmeno guardato così in basso. Join after start again :( –

+0

Questo è un miglioramento incrementale, ma non importa quali siano i thread esistenti di Python sono orribili.Potremmo raccomandare il multiprocessing, vedere la mia risposta –

+0

@Mike: questo non è un miglioramento incrementale del tutto, utilizzando il codice MarkZar fornito, ha migliorato i tempi di esecuzione nei miei test da circa 20 secondi a meno di mezzo secondo, poiché http utilizza una CPU minima ma è estremamente sensibile alla latenza di rete, quindi usare 'threading' invece di' multiprocessing' è un soluzione assolutamente ragionevole, che va raddoppiata se fosse utilizzato un client http Keep-Alive ('urlib3' era circa il 30% più veloce rispetto a 'urllib2' nei miei test di thread fisso, nessun miglioramento in altro modo), che non sarebbe disponibile tra i processi. – SingleNegationElimination