2012-04-14 2 views
6

Теперь я изучаю, как быстро получать данные с сайта. Чтобы получить более высокую скорость, я рассматриваю возможность использования многопоточных потоков. Вот код, который я использовал для проверки разницы между многопоточной и простой почтой.Как получить более высокую скорость при использовании многопоточности в 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 

как вы можете видеть, это очень простой код. сначала я устанавливаю режим на «Простой», и я могу получить временной интервал: 50s (возможно, моя скорость немного медленная :(), тогда я устанавливаю режим «Множественный», и я получаю временной интервал: . от я могу видеть, многопоточные может увеличить скорость, но результат разве так хорошо, как я себе представить. я хочу, чтобы получить гораздо более высокую скорость.

от отладки, я обнаружил, что программа в основном блоки на линии: open_url = urllib2.urlopen(req, self.data), эта строка кода занимает много времени, чтобы отправлять и получать данные с указанного веб-сайта. Возможно, я могу получить более быструю скорость, добавив time.sleep() и используя многопоточность внутри функции urlopen, но Я не могу этого сделать, потому что его собственная функция python.

Если не учитывать допустимые пределы, которые сервер блокирует скорость сообщения, что еще я могу сделать, чтобы получить более быструю скорость? или любой другой код, который я могу изменить? большое спасибо!

+1

потоковое это плохая идея в питоне, он получает легко упирается и может попасть в ловушку в GIL, попробуйте мультипроцессирование. –

+1

@JakobBowyer: потоки представляют собой деталь реализации, реальный фокус - открывать несколько соединений. Аспект GIL для потоковой передачи в Python не имеет здесь никакой роли. – orlp

+0

@ nightcracker, вы действительно должны прочитать GIL и threading перед тем, как сделать такие заявления ... начать здесь: [PyCon 2010: понимание Python GIL] (http://python.mirocommunity.org/video/1479/pycon- 2010-understanding-the-p) –

ответ

5

Во многих случаях, нарезание резьбы питона не улучшает скорость выполнения очень хорошо ... иногда, это делает его хуже. Для получения дополнительной информации см. David Beazley's PyCon2010 presentation on the Global Interpreter Lock/Pycon2010 GIL slides.Эта презентация очень информативна, я очень рекомендую ее любому, кто рассматривает резьбу ...

Вы должны использовать multiprocessing module. Я включил это как вариант в ваш код (см. Нижнюю часть моего ответа).

Запуск этого на одном из моих старых машин (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 

Я согласен с комментарием TokenMacGuy и цифры выше, включают перемещение .join() в другую петлю. Как вы можете видеть, многопроцессорность python значительно быстрее, чем потоки.


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 много. многопроцессорность - хорошая идея, ее действительно немного быстрее, чем многопоточность на моем компьютере. всех вас. Я многое узнал из вопроса. – Searene

+0

@MarkZar, я бы сказал, что 33% -ное улучшение скорости больше, чем немного быстрее, но, несмотря на то, что я желаю вам успеха в вашем проекте. –

0

Поиск DNS требует времени. Вы ничего не можете с этим поделать. Большие задержки - одна из причин использования нескольких потоков в первую очередь - параллельный поисковый сайт GET/POST.

Дамп сон() - это не помогает.

+0

Thx, но я только путаю, почему 'time.sleep()' бесполезно. Действительно, он также хорошо работает после сброса 'sleep()', но как он может реализовать многопоточность без 'sleep()'? выполняет ли python различные потоки в случайных интервалах автоматически? если да, то что использует функция 'sleep()'? – Searene

+0

Это бесполезно, просто неуместно. Использование сна - есть нагрузки. «После включения насоса подождите не менее 10 секунд, чтобы давление стабилизировалось, прежде чем открывать подающий клапан». –

0

Имейте в виду, что единственный случай, когда многопоточность может «увеличивать скорость» в Python, - это когда у вас есть операции , как этот, которые связаны с большими объемами ввода-вывода. В противном случае многопоточность не увеличивает «скорость», поскольку она не может работать на нескольких процессорах (нет, даже если у вас несколько ядер, python не работает именно так). Вы должны использовать многопоточность, если хотите, чтобы две вещи выполнялись одновременно, а не когда вы хотите, чтобы две вещи были параллельными (т. Е. Два процесса выполнялись отдельно).

Теперь, что вы на самом деле делаете, на самом деле не увеличит скорость ни одного поиска в DNS, но это позволит раскрутить несколько запросов, ожидая результатов некоторых других, но вы должны быть осторожны сколько вы делаете, или вы просто сделаете время ответа еще хуже, чем они есть.

Также, пожалуйста, прекратите использование urllib2, и использовать запросы: http://docs.python-requests.org

7

Самое большое, что вы делаете неправильно, что вредит вашей пропускной способности больше всего, это то, как вы вызываете thread.start() и thread.join():

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

Каждый раз, проходя через цикл, вы создаете поток, запускаете его, а затем ждите его завершения. Перед тем, как перейти к следующей теме. Вы ничего не делаете одновременно!

То, что вы, вероятно, следует делать вместо этого:

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

Я даже не выглядел так далеко. Присоединиться после старта снова :( –

+0

Это постепенное улучшение, но независимо от того, какие существующие потоки python ужасны. Мы должны рекомендовать многопроцессорность, см. Мой ответ. –

+0

@Mike: это не постепенное улучшение вообще, используя Code MarkZar Это улучшило время выполнения в моих тестах примерно с 20 секунд до менее половины секунды. Это имеет смысл, поскольку http использует минимальный процессор, но очень чувствителен к задержке в сети, и поэтому использование 'threading' вместо' multiprocessing' является вполне разумное решение. Это удваивается, если используется клиент-клиент Keep-Alive ('urlib3' был на 30% быстрее, чем' urllib2' в моих фиксированных тестах на потоки, в противном случае улучшения не было), которые не будут доступны для всех процессов. – SingleNegationElimination

Смежные вопросы