Уведомления

Группа в Telegram: @pythonsu

#1 Дек. 11, 2012 16:30:59

reclosedev
От: Н.Новгород
Зарегистрирован: 2012-03-29
Сообщения: 870
Репутация: +  173  -
Профиль   Отправить e-mail  

httplib2 низкая результативность при увеличении потоков.

Soteric
Хорошо бы профилировщиком посмотреть на что тратится время, но я к сожалению не силен в профилировании питон приложений.
Кстати, да. Нужно профилировать. Все очень просто:
python -m cProfile -o profiler.dat your_application.py
pyprof2html profiler.dat
pyprof2html - для удобного просмотра результатов в браузере.

А по коду, который показали, что-то подсказать сложно, но смущает работа с очередями. В Queue уже есть синхронизируемые методы get(), put(), зачем использовать дополнительный RLock и get/put_nowait()?

Офлайн

#2 Дек. 11, 2012 16:32:45

DHT
От:
Зарегистрирован: 2009-09-24
Сообщения: 119
Репутация: +  0  -
Профиль   Отправить e-mail  

httplib2 низкая результативность при увеличении потоков.

# -*- coding: utf-8 -*-
import Queue
import threading
import httplib2
import time
# для оценки скорости
success = 0
time_start = time.time()
def worker():
    """ код потока """
    global success
    global time_start
    h = httplib2.Http()
    h.timeout = 10
    while True:
        time.sleep(0.01)
        try:
            u = q.get_nowait()
            (r, c) = h.request(u, "GET")
            success += 1
            print("{0:.2f}".format(success/(time.time() - time_start)))
        except:
            pass
# создать очередь 
q = Queue.Queue()
# создать потоки
for i in range(50):
    t = threading.Thread(target=worker)
    t.setDaemon(True)
    t.start()
# внести задания в очередь
file = open('u.txt', 'r')
for i in file.readlines():
    q.put(i.rstrip('\r\n'))
file.close()
# ждемс
q.join()

Вот простейшая реализация. Может кто-нибудь захочет протестровать скорость на своем железе.
Файл заданий (u.txt) прилагается.



Отредактировано DHT (Дек. 11, 2012 16:40:14)

Прикреплённый файлы:
attachment u.txt (38,3 KБ)

Офлайн

#3 Дек. 11, 2012 16:51:24

Soteric
От:
Зарегистрирован: 2010-09-19
Сообщения: 352
Репутация: +  20  -
Профиль   Отправить e-mail  

httplib2 низкая результативность при увеличении потоков.

time.sleep(0.01)
зачем это? и зачем get_nowait?

Поток должен делать блокирующий get() и вставать если в очереди пусто. По завершении задачи он должен делать на очереди task_done() и снова get(). Тогда основной поток, стоящий в q.join(), будет разбужен когда число сделанных task_done станет равным числу сделанных put (то есть когда все положенные в очередь задачи будут выполнены).



Отредактировано Soteric (Дек. 11, 2012 16:57:49)

Офлайн

#4 Дек. 11, 2012 16:58:33

DHT
От:
Зарегистрирован: 2009-09-24
Сообщения: 119
Репутация: +  0  -
Профиль   Отправить e-mail  

httplib2 низкая результативность при увеличении потоков.

Soteric
зачем это?
На скорость работы существенно не влияет, а нагрузку на процессор снимает значительно.

… убрал timeout и сделал как выше сказано, но не это тормозит процесс.
# -*- coding: utf-8 -*-
import Queue
import threading
import httplib2
import time
# для оценки скорости
success = 0
time_start = time.time()
def worker():
    """ код потока """
    global success
    global time_start
    h = httplib2.Http()
    h.timeout = 10
    while True:
        try:
            u = q.get()
            (r, c) = h.request(u, "GET")
            success += 1
            q.task_done()
            print("{0:.2f}".format(success/(time.time() - time_start)))
        except:
            pass
# создать очередь 
q = Queue.Queue()
# создать потоки
for i in range(50):
    t = threading.Thread(target=worker)
    t.setDaemon(True)
    t.start()
# внести задания в очередь
file = open('u.txt', 'r')
for i in file.readlines():
    q.put(i.rstrip('\r\n'))
file.close()
# ждемс
q.join()



Отредактировано DHT (Дек. 11, 2012 17:11:21)

Офлайн

#5 Дек. 11, 2012 17:51:48

reclosedev
От: Н.Новгород
Зарегистрирован: 2012-03-29
Сообщения: 870
Репутация: +  173  -
Профиль   Отправить e-mail  

httplib2 низкая результативность при увеличении потоков.

В данном случае (множество независимых серверов) скорость и оптимальное количество потоков зависит от канала.
Например, на домашнем ADSL 5Mbit
при 50 - еле дотягивает до 2 url/sec
при 15 - приближается к 4 url/sec

На сервере 100 Mbit
50 - 40-50 url/sec
100 - не дает особого прироста

Если действительно нужна скорость и есть хороший канал, смотрите в сторону асинхронных библиотек.
Вот код на grequests (под капотом greenlet, gevent, requests)

# -*- coding: utf-8 -*-
import grequests
import time
  
SIMULTANEOUS = 200 
started = time.time()
 
with open('u.txt', 'r') as f:
    urls = f.read().splitlines()
 
reqs = (grequests.get(url, timeout=10) for url in urls)
for i, resp in enumerate(grequests.imap(reqs, size=SIMULTANEOUS)):
    if resp.ok:
        print("{0}/{1} {2:.2f}".format(i, len(urls), i/(time.time() - started)))
        # Можно делать что-то с resp.content или resp.text
        # Нужно учесть, что очередность при imap не соблюдается,
        # но есть resp.url А еще можно пользоваться hook'ами (колбэками)
Скорости на канале 100 Mbit:
50 - 50-60 url/sec
100 - ~100 url/sec
200 - ~140 url/sec

Помимо grequests есть scrapy, grab.Spider, tornado и т.п. А если уже много кода на httplib2 и менять не хочется, можно попробовать gevent.

Отредактировано reclosedev (Дек. 11, 2012 17:52:38)

Офлайн

#6 Дек. 11, 2012 19:11:27

DHT
От:
Зарегистрирован: 2009-09-24
Сообщения: 119
Репутация: +  0  -
Профиль   Отправить e-mail  

httplib2 низкая результативность при увеличении потоков.

reclosedev
На сервере 100 Mbit50 - 40-50 url/sec
а что за сервер? просто у меня тоже 100 Mbit, но 40-50 не дает.



Офлайн

#7 Дек. 11, 2012 20:22:44

Lexander
От:
Зарегистрирован: 2008-09-19
Сообщения: 1139
Репутация: +  33  -
Профиль   Отправить e-mail  

httplib2 низкая результативность при увеличении потоков.

DHT
Если вместо запроса по HTTP сделать какое-нибудь простое дейтсвие, то потоки отрабатывают 10ки тысяч записей из очереди в секунду. Т.е. на реализацию работы с очередями я не жалуюсь. Жалусь лишь на httplib2.
Простое действие не дает простаивать потокам.

В вашем случае при большом количестве наступает “насыщение”, когда Питон на обслуживание потоков тратит больше времени, чем занимает работа.
Кроме того, ваш код включает во время выполнения потоков затраты на обслуживание исключений. Я поймал три разных - от socket.timeout и httplib2.RedirectLimit до Queue.empty для разного кол-ва потоков.

Вместо threading лучше используйте multiprocessing или даже что-то асинхронное типа gevent.



Офлайн

#8 Дек. 11, 2012 20:28:32

reclosedev
От: Н.Новгород
Зарегистрирован: 2012-03-29
Сообщения: 870
Репутация: +  173  -
Профиль   Отправить e-mail  

httplib2 низкая результативность при увеличении потоков.

DHT
а что за сервер?
Самый дешевый VPS от Хетзнер с Ubuntu.
$ cat /proc/cpuinfo | grep MHz
cpu MHz : 3411.486
DHT
40-50 не дает.
Если подольше держать, тоже меньше числа показывает. Но это, возможно, из-за способа расчета.

Офлайн

#9 Дек. 11, 2012 20:37:36

DHT
От:
Зарегистрирован: 2009-09-24
Сообщения: 119
Репутация: +  0  -
Профиль   Отправить e-mail  

httplib2 низкая результативность при увеличении потоков.

reclosedev
Если подольше держать, тоже меньше числа показывает.
Вот-вот! На старте у меня тоже хорошая скорость, но потом замедляется и игра с таймаутами ничем не помогает.



Офлайн

#10 Дек. 12, 2012 09:34:01

Soteric
От:
Зарегистрирован: 2010-09-19
Сообщения: 352
Репутация: +  20  -
Профиль   Отправить e-mail  

httplib2 низкая результативность при увеличении потоков.

Можно собрать дополнительную статистику по времени выполнения запроса. Выделить максимальное время, за которое выполняется 99% запросов (или другое критичное значение). Установить значение реквест таймаута немного выше этой величины. Таким образом откровенно тормозные запросы не будут портить статистику. Потому что судя по “начало нормальное, а потом все тормозит” можно предположить, что со временем все больше и больше потоков оказываются вовлечены в парсинг очередного “тормозного” сайта. Плюс то, что сказал Lexander: в какой-то момент от увеличения потоков производительность начнет падать из-за того, что процессор будет проводить основную часть времени на переключении между потоками.



Отредактировано Soteric (Дек. 12, 2012 09:37:07)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version