2012-04-14 7 views
6

Ahora estoy estudiando cómo recuperar datos del sitio web lo más rápido posible. Para obtener una velocidad más rápida, estoy considerando usar múltiples hilos. Aquí está el código que utilicé para probar la diferencia entre la publicación de subprocesos múltiples y simple.Cómo obtener una velocidad más rápida al usar multi-threading en 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 

tal como puede ver, este es un código muy simple. primero establezco el modo en "Simple", y puedo obtener el intervalo de tiempo: 50s (tal vez mi velocidad es un poco lenta :(). Luego configuro el modo a "Múltiple", y obtengo el intervalo de tiempo: . de eso puedo ver, multi-hilo puede aumentar la velocidad, pero el resultado no es tan bueno como me imagino. Quiero obtener una velocidad mucho más rápida

de depuración, encontré que el programa principalmente bloques en la línea: open_url = urllib2.urlopen(req, self.data), esta línea de código toma mucho tiempo para publicar y recibir datos del sitio web especificado. Supongo que tal vez pueda obtener una velocidad más rápida agregando time.sleep() y usando multi-threading dentro de la función urlopen, pero No puedo hacer eso porque es la función de la pitón.

si no se consideran los límites posibles de que el servidor bloquee la velocidad de publicación, ¿qué más puedo hacer para obtener la velocidad más rápida? o cualquier otro código que pueda modificar? ¡muchas gracias!

+1

enhebrado es una mala idea en Python, el fichero es un cuello de botella con facilidad y puede quedar atrapado por el GIL, trate de multiprocesamiento. –

+1

@JakobBowyer: los hilos son un detalle de implementación aquí, el enfoque real es tener múltiples conexiones abiertas. El aspecto GIL de enhebrar en Python no tiene ningún papel aquí. – orlp

+0

@nightcracker, realmente debería leer GIL y enhebrar antes de hacer declaraciones como esa ... comience aquí: [PyCon 2010: Understanding the Python GIL] (http://python.mirocommunity.org/video/1479/pycon- 2010-understanding-the-p) –

Respuesta

5

En muchos casos, roscado del pitón no mejora la velocidad de ejecución muy bien ... a veces, se hace peor. Para obtener más información, consulte David Beazley's PyCon2010 presentation on the Global Interpreter Lock/Pycon2010 GIL slides.Esta presentación es muy informativa, lo recomiendo encarecidamente a cualquiera que esté pensando en enhebrar ...

Debe usar el multiprocessing module. Incluí esto como una opción en tu código (ver la parte inferior de mi respuesta).

La ejecución de esta en una de mis máquinas más antiguas (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 

Estoy de acuerdo con el comentario de TokenMacGuy y los números anterior incluyen mover el .join() a un circuito diferente. Como puede ver, el multiprocesamiento de Python es significativamente más rápido que el de subprocesamiento.


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 mucho. el multiprocesamiento es una buena idea, de hecho es un poco más rápido que el multi-threading en mi computadora. todos ustedes Aprendí mucho de la pregunta. – Searene

+0

@MarkZar, yo diría que una mejora del 33% en la velocidad es más que un poco más rápida, pero a pesar de que le deseo lo mejor en su proyecto. –

0

Una búsqueda de DNS lleva tiempo. No hay nada que puedas hacer al respecto. Las latencias grandes son una de las razones para utilizar varios subprocesos: el sitio de anuncios de múltiples búsquedas GET/POST puede suceder en paralelo.

Volcar el sueño() - no ayuda.

+0

Thx, pero solo confundí por qué 'time.sleep()' es inútil. De hecho, también funciona bien después de descargar 'sleep()', pero ¿cómo puede realizar multi-thread sin 'sleep()'? ¿Python ejecuta diferentes subprocesos a intervalos aleatorios automáticamente? si es así, ¿cuál es el uso de la función 'sleep()'? – Searene

+0

No es inútil, simplemente inapropiado aquí. Uso del sueño: hay cargas. 'Después de encender la bomba, espere al menos diez segundos para que la presión se estabilice antes de abrir la válvula de alimentación'. –

0

Tenga en cuenta que el único caso donde multi-threading puede "aumentar la velocidad" en Python es cuando tiene operaciones como esta que están fuertemente vinculadas a E/S. De lo contrario, el multi-threading no aumenta la "velocidad", ya que no puede ejecutarse en más de una CPU (no, ni siquiera si tiene múltiples núcleos, Python no funciona de esa manera). Debería usar varios subprocesos cuando quiera hacer dos cosas al mismo tiempo, no cuando desee que dos cosas sean paralelas (es decir, dos procesos que se ejecutan por separado).

Ahora, lo que realmente está haciendo en realidad no aumentará la velocidad de una única búsqueda de DNS, pero permitirá que varias solicitudes se dispare mientras espera los resultados de algunos otros, pero debe tener cuidado de cuántos hace o simplemente hará que los tiempos de respuesta sean aún peores de lo que ya son.

También por favor dejen de usar urllib2, y el uso de Solicitudes: http://docs.python-requests.org

7

La cosa más grande que está haciendo mal, que está perjudicando su rendimiento al máximo, es la forma en la que está llamando thread.start() y thread.join():

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

Cada vez que pasa por el ciclo, crea un hilo, lo inicia y luego espera a que finalice Antes de pasar al siguiente hilo. ¡No estás haciendo nada al mismo tiempo!

Lo que probablemente debería estar haciendo en su lugar es:

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

Ni siquiera miré tan abajo. Únete después de comenzar de nuevo :( –

+0

Esto es una mejora incremental, pero no importa qué subprocesos existentes de python son terribles. Deberíamos recomendar el multiprocesamiento; ver mi respuesta. –

+0

@Mike: esto no es una mejora incremental, usando el Código MarkZar siempre, mejoró el tiempo de ejecución en mis pruebas de alrededor de 20 segundos a menos de medio segundo. Esto tiene sentido, ya que http usa una CPU mínima pero es muy sensible a la latencia de la red, por lo que usar 'threading' en lugar de' multiprocessing' es solución totalmente razonable. Esto se duplica si se utilizara un cliente HTTP Keep-Alive ('urlib3' era aproximadamente 30% más rápido que' urllib2' en mis pruebas de subprocesamiento fijas, sin ninguna mejora), que no estaría disponible en todos los procesos. – SingleNegationElimination

Cuestiones relacionadas