Найти - Пользователи
Полная версия: Network. GIL, threads и green threads.
Начало » Python для экспертов » Network. GIL, threads и green threads.
1 2 3
Lexander
skavans
eventlet по каким-то причинам получился быстрее.. ниже привожу код, который я прогонял
Вполне может быть. Вас не устраивает общая скорость сканирования?
У вас, судя по диапазону, около 16000 адресов, которые обрабатываются за приемлемое время.
Если бы были миллионы или миллиарды, то тогда бы можно было что-то думать.

Обратите внимание:
1. тест на 1 ядре
2. замеренное время на 90% состоит из работы модуля socket, а не gevent или eventlet
Процент навскидку, просто для понимания результатов замера.

Я сейчас не могу запускать ваши тесты и/или написать свой код.
В принципе, код в части использования асинхронных библиотек - нормальный.
Для eventlet тоже можно использовать такую же конструкцию при заполнении пула, как и для gevent.

Код работы с сокетом можно чуть улучшить, но есть ли в этом смысл?
Вместо connect в случае работы с множеством сокетов лучше использовать connect_ex, который вместо исключения возвращает код ошибки.
Исключение - это довольно дорогая конструкция. при проблемах связи они будут сыпаться как из ведра и серьезно затормозят работу. Конечно не на 16000, на бОльших количествах.
Кстати, вы смотрели сколько было исключений?

Ну, и исключение для итератора режет взгляд.
Хотя эта часть менее критична, т.к. работает максимум 1 раз для каждого гринлета из пула.
bw
skavans> Какую библиотеку стоит использовать из множества (gevent, eventlet, coroutines, etc.)? … Вопросы сложности написания кода под конкретную библиотеку не рассматриваются…
1. socket + select = asyncore.
2. Если считаете что использовать низкоуровневые батарейки не спортивно, то посмотрите в сторону событийных движков, например, есть такой чудной зверь как Circuits. Так же на глаза попадались Pulsar и Pants. Есть ещё whizzer. Этот к сожалению не развивается, а тот экземпляр что я пробовал (то ли из git, то ли релизную версию) имел примитивные позорные ошибки на грани опечаток. Заинтересовал API, например в нём введено понятие протокола, сервера и пр., всё как у большого брата Twisted, только этот с сердцем на pyev. В принципе API подсмотрен у Twisted, это очевидно.
3. Есть легковестный микронитевой фреймворк Diesel с занятной архитектурой, жаль он тянет за собой кучу левого не нужного барахла, а так же с абстракцией у него не очень. Я взял его на вооружение, когда занимался своим велосипедом. Так же в один ряд с gevent, eventlet и concurrence не забываем поставить syncless.

Есть ещё маленькая полезняшка iowait, как раз по вашей теме, если select'ами брезгуите, всё же XXI век на дворе :-). И ещё blinker есть, приятная мелочушка для мелочей, это сигнальная тема, никак с сетью не связанная.

skavans> А в чем различия между вот таким переключением зеленого потока и асинхронностью? Везде же некоторое подобие event loop используется, я прав?
Да, вы правы. В событийных (Twisted) и потоковых (Eventlet) системах присутствует “event loop”, в первом случае он явный, во втором шифруется за блокирующими операциями, такими как recv, send, sleep и пр. (за вызовом таких блокирующих операцией скрывается select или libevent, libev, libuv с последующем переключением потока куда нужно). Потоковые должны быть медленнее и больше употреблять памяти из-за необходимости хранить стеки и переключать управление между потоками, но работать с ними зачастую проще/естественнее, чем с событийными.

Так же можете попробовать собрать Python без потоков, теоретически это может дать прирост.

..bw
skavans
bw, спасибо за развернутый ответ!

bw
1. socket + select = asyncore.
2. Если считаете что использовать низкоуровневые батарейки не спортивно,

Я совсем не против низкоуровневой реализации, напротив - мне кажется, что для такой простой задачи (открыть сокет, попробовать законнектиться, посмотреть результат), где основной упор должен быть на скорость работы, необходимо наиболее низкоуровневое решение. Однако, используя тот же asyncore, я не смог добиться необходимого результата. Вот код, который я использовал:

import socket
from iptools import IpRange
import asyncore
class ConnectionTester(asyncore.dispatcher):
    def __init__(self, host):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.settimeout(5)
        self.connect( (host, 80) )
    def handle_connect(self):
        print 'ok'
    def handle_error(self):
        print 'bad'
guys = [ConnectionTester(ip) for ip in IpRange('62.64.64.0', '62.64.64.5')]
asyncore.loop()

Этот код выполняется последовательно, что мне кажется очевидным - ведь ioloop запускается уже после того, как сокеты созданы генератором. Не понял, как реализовать асинхронный коннект в данном случае.
bw
Что вы конкретно под последовательно выполняете? Вы ведь не думаете, что если вы с двух реальных ядер CPU, сделав два одновременных (с точностью syscall-а до такта) обращения к ядру системы (допустим что ядро так же работает на двух CPU), получите два одновременных соединения? Вынужден ваз разочаровать, ядро не однократно прибегнет к блокировке (если оно вообще в несколько потоков работает) на разных уровнях что приведёт к последовательной посылке датаграм по eth. Но подозреваю, что вы это и так понимаете.
Вы про select (мультиплексный ввод-вывод) слышали?
Неблокируемый сокет не блокирует поток в момент блокирующего обращения к ядру.
Попробуйте settimeout убрать, а так код в порядке, должен работать лучше чем Eventlet. Получилось на каком уровне в сравнении с другими вариантами?

p.s. Можно ещё книжку Р.Стивенса “UNIX разработка сетевых приложений” почитать.

..bw
skavans
bw, понимаю, конечно дело в том, что таймаут мне убрать нельзя - отключение по таймауту входит в постановку задачи.
Проблема в том, что если в handle_connect выполнять некоторые блокирующие операции (например, получение содержимого страницы), то asyncore.loop() позволит выполнять это параллельно с нескольких сокетов (псевдопараллельно, если быть точнее). А вот псевдопараллельный connect с помощью asyncore у меня сделать не получилось. Потому что сокеты нужно сначала создать и законнектить, а потом уже запускать ioloop. Вот на микронитях или событиях получается и коннект параллелить, а asyncore такой возможности как-будто не дает

На select/poll/epoll вот еще не пробовал задачу решить, немного другими вещами сейчас занимаюсь, пока что вариант на eventlet устроил, но все равно хочется еще большей скорости достичь, поэтому все-таки попробую их применить.

P.S.: а без таймаутов все нормально работает в таком коде, псевдопараллельно. Просто, как я понял, методы сокета setblocking и settimeout - взаимозаменяемые, нельзя использовать их одновременно. Поэтому вроде как мне для данной задачи не подходят неблокирующие сокеты, а только event-looping или микронити.
bw
> Потому что сокеты нужно сначала создать и законнектить, а потом уже запускать ioloop.
Соединение как и посыл/получение выполняется асинхронно (ниже ещё про это скажу).

> На select/poll/epoll вот еще не пробовал задачу решить
Уже попробовали – asyncore, eventlet, ну вы меня поняли ;-).

> Поэтому вроде как мне для данной задачи не подходят неблокирующие сокеты, а только event-looping или микронити.
Как я написал раньше, они всё равно на неблокирующих сокетах, даже если цикл фреймворка построен на libevent, libev или libuv, используются неблокирующие сокеты и select/poll/epoll.

Про settimeout я спросил так как что-то такое в памяти теплится, что они ломают неблокируемость самого соединения. И насколько я знаю все эти сетевые фреймворки не используют socket.settimeout для таймаутов, а реализовывают их (таймауты :-) самостоятельно.

..bw
skavans
bw, ну вот в случае моего кода на asyncore - соединение последовательное, генератор при создании объекта коннектит сокет. Последовательно Видимо, вся соль этих фреймворков в самостоятельной реализации таймаутов как раз.. а по поводу того, что не пробовал select etс. имел в виду, что не пробовал на низком уровне решить задачу с их помощью
bw
Посмотрел исходники Python 2.7.3 (читать документацию не круто же :-), socket.settimeout делает сокет блокируемым. Это чисто питонячья фича. Можно попробовать вместо неё использовать SO_RCVTIMEO и SO_SNDTIMEO, правда на connect они не влияют. В принципе не очень сложно и свои таймауты сделать, если очень надо. По крайней мере меня бы это не остановило :-).

..bw
o7412369815963
bw
… потоковых (Eventlet) системах присутствует “event loop”, … Потоковые должны быть медленнее и больше употреблять памяти из-за необходимости хранить стеки
Лучше называть как “зеленые потоки” что-б с обычными не путать.

bw
Как я написал раньше, они всё равно на неблокирующих сокетах, даже если цикл фреймворка построен на libevent, libev или libuv, используются неблокирующие сокеты и select/poll/epoll.
Это имеет значение, epoll более производительное, лучше работает с большым кол-вом соединений.
Поэтому “socket + select” может уступать по производительности libev.
Lexander
Не забывайте, что автор сейчас работает только с 1 ядром процессора.
Если есть запас пропускной способности сетевого интерфейса, уверен, при запуске зеленых нитей в нескольких потоках, будет прирост производительности.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB