О stackless python

158 views
Skip to first unread message

vysster

unread,
Oct 30, 2010, 9:52:10 AM10/30/10
to ru-python-async
Добрый день, Александр!

Работая с python-ом натыкался на похожие проблемы. Когда посмотрел
stackless (тогда это был такой огромный патч к основной ветке), то
этот подход меня немного ошарашил - поддерживать ветку было бы сложно
даже автору, не говоря уж о других людях. С этим можно было бы жить,
переписав весь код с нуля. Но в этом случае есть риск привнести в
реализацию что-то своё (запуск релиза отложился бы на года, ведь
заморозили ветку CPython не просто так).

Это был демотиватор, теперь мотиватор: в любом случае python будет
идти путем, известным одному только GvR. Почему бы в таком случае не
продумать архитектуру приложения, оставив совместимость только по
синтаксису. Так или иначе, совместимость со сторонними модулями будет
ломается. Мне кажется, что не стоит тешить себя иллюзией, что это
будет Python на thread-стеройдах. Это будет совершенно новый язык.
Думаю что люди, пишущие для CPython, прекрасно отдавали себе в этом
отчет, потому и не ступили на этот путь.

Индикатором того, что мои слова близки к правде можно обратившись к
истории развития ipython и jython - эти обреченные быть вечно-
устаревшими по отношению к базовому функционалу. Да, обеспечение
совместимости - это хорошо, когда стараешься достичь какого-то идеала
среди языков программирования. Python же не идеал, хотя у него есть
чему поучиться - много интересных технических решений.

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

Это все - лишь мое личное мнение. Хотелось бы узнать ваши мысли.

Alexander Lourier

unread,
Oct 30, 2010, 10:51:22 AM10/30/10
to ru-pyth...@googlegroups.com
On Saturday 30 October 2010 17:52:10 vysster wrote:

> Работая с python-ом натыкался на похожие проблемы. Когда посмотрел
> stackless (тогда это был такой огромный патч к основной ветке), то
> этот подход меня немного ошарашил - поддерживать ветку было бы сложно
> даже автору, не говоря уж о других людях. С этим можно было бы жить,
> переписав весь код с нуля. Но в этом случае есть риск привнести в
> реализацию что-то своё (запуск релиза отложился бы на года, ведь
> заморозили ветку CPython не просто так).

А сейчас разве не так? Stackless постоянно синхронизируется с основной веткой:
есть версии для 2.6, 2.7 и 3.1. Главный разработчик Stackless работает в CCP
Games (разработчик EVE Online), его там кормят, и покуда это так, проект он
точно не бросит.

> Это был демотиватор, теперь мотиватор: в любом случае python будет
> идти путем, известным одному только GvR. Почему бы в таком случае не
> продумать архитектуру приложения, оставив совместимость только по
> синтаксису. Так или иначе, совместимость со сторонними модулями будет
> ломается. Мне кажется, что не стоит тешить себя иллюзией, что это
> будет Python на thread-стеройдах. Это будет совершенно новый язык.
> Думаю что люди, пишущие для CPython, прекрасно отдавали себе в этом
> отчет, потому и не ступили на этот путь.

Если вы пишете, например, форумный движок, который будут ставить у себя
пользователи на разных системах, то тут пожалуй да - писать на Python - это
вообще обрекать себя на постоянную головную боль с обратной совместимостью
языка. Тогда всё вышесказаное справедливо на 100%.

Наши проекты хостятся на серверах, где конфигурацию окружения мы выбираем
сами. И такую, какая нам удобна. Иными словами, проект стартовал на Stackless
2.6, под эту версию мы затачиваем актуальные версии библиотек, и осуществлять
какие-то переходы на 2.7, 3.1 или, упаси бог, 4.0 не планируем. Stackless -
это именно Python на стероидах - столь же удобная разработка при более
высокой производительности.

К слову, портировать сетевые библиотеки на Stackless оказывается достаточно
просто - подменили сокеты, заставили работать чтение и запись через буферы, и
огонь. Сложности возникают только с протоколами, реализованными на C, типа
libmysqlclient, - там только полное переписывание.

> Если ставится задача набраться опыту в разработке новых языков
> программирования, то попытаться написать что-то под уже работающую
> платформу - это один из лучших способов самообучения.

Наша задача чисто инженерная - разработка в кратчайшие сроки
высокогонагруженных проектов, чтобы они работали быстро и безошибочно.
Stackless - это не академический проект, это вполне себе рабочая лошадка,
которую мы успешно оседлали, и хотим поделиться знаниями с сообществом.

Спасибо за проявленный интерес :)

vysster

unread,
Oct 30, 2010, 2:00:47 PM10/30/10
to ru-python-async

On Oct 30, 6:51 pm, Alexander Lourier <a...@rulezz.ru> wrote:
> Наша задача чисто инженерная - разработка в кратчайшие сроки
> высокогонагруженных проектов, чтобы они работали быстро и безошибочно.
> Stackless - это не академический проект, это вполне себе рабочая лошадка,
> которую мы успешно оседлали, и хотим поделиться знаниями с сообществом.

Интересно, коли он настолько подрос. Спасибо за наводку :)

Роман Кривенков

unread,
Dec 12, 2012, 5:30:50 AM12/12/12
to ru-pyth...@googlegroups.com
Заинтересовался Stackless Python.
1) На сколько я понял, любые сетевые библиотеки, написанные на чистом питоне, в нем сразу заработают правильно.
Мне нужна поддержка базы PostgreSQL, есть ли драйвер, аналогичный драйверу MySQL в Concurrence?
2) Какие новые библиотеки разрабатываются под  Stackless Python?
3) Зачем в Concurrence используется libevent, если интерпритатор сам умеет переключать тасклеты?

Alexander Lourier

unread,
Dec 12, 2012, 6:20:56 AM12/12/12
to ru-pyth...@googlegroups.com
12.12.2012 14:30, Роман Кривенков пишет:

> Заинтересовался Stackless Python.
> 1) На сколько я понял, любые сетевые библиотеки, написанные на чистом
> питоне, в нем сразу заработают правильно.

Они заработают. А насколько это будет правильно - зависит уже от того,
что они делают. Если они будут блокироваться, то исполнение всего
приложения тоже заблокируется. Если они делают что-то вычислительно
очень лёгкое и весь ввод-вывод перенесён на сокеты concurrence, то всё
заработает.

> Мне нужна поддержка базы PostgreSQL, есть ли драйвер, аналогичный
> драйверу MySQL в Concurrence?

Насколько мне известно, поддержку PostgreSQL ещё никто не реализовал.

> 2) Какие новые библиотеки разрабатываются под Stackless Python?

Я кое-что пишу, по мере надобности.
https://github.com/Joyteam/concurrence
Про других разработчиков не знаю.

> 3) Зачем в Concurrence используется libevent, если интерпритатор сам
> умеет переключать тасклеты?

libevent - это возможность привязать обработчики к асинхронным событиям.
По сути, используется для ввода-вывода и таймеров. Пришли данные в сокет
(либо сработал таймер, либо освободилось место для записи в сокет) -
дёргается событие, пихаются какие-то данные в канал, и будится какой-то
тасклет.

--
Alexander Lourier, http://aml.rulezz.ru
Message has been deleted

Роман Кривенков

unread,
Dec 12, 2012, 9:22:08 AM12/12/12
to ru-pyth...@googlegroups.com
Сейчас я использую свой самописный сервер, пишу асинхронно в псевдо-синхронном стиле на генераторах.
Недавно уперся в глубину стека. Я конечно обошел это, но осадок остался, и подход Stackles Python выглядит гораздо лучше.

Мой сетевой ввод-вывод сделан через epoll. Переписать его на сокеты concurrence не проблема.
А вот что делать с PostgreSQL.. Хоть самому не пиши.

Сейчас я пользуюсь асинхронным драйвером postgresql, и fileno у его сокетов отдаю в epoll.
В доке написано, что его можно использовать с gevent и eventlet. Есть идеи, как это можно прикрутить к Concurrence?

Alexander Lourier

unread,
Dec 14, 2012, 3:23:18 AM12/14/12
to ru-pyth...@googlegroups.com
12.12.2012 18:22, Роман Кривенков пишет:

> А вот что делать с PostgreSQL.. Хоть самому не пиши.
> Сейчас я пользуюсь асинхронным драйвером postgresql, и fileno у его
> сокетов отдаю в epoll.
> В доке написано, что его можно использовать с gevent и eventlet. Есть
> идеи, как это можно прикрутить к Concurrence?
> http://www.initd.org/psycopg/docs/advanced.html#asynchronous-support
> <http://www.initd.org/psycopg/docs/advanced.html#asynchronous-support>

Идеи, конечно, есть. Посмотрите, как в Concurrence реализована реакция
на события "сокет готов к чтению данных".

В lib/concurrence/core.py описан класс FileDescriptEvent:

def __init__(self, fd, rw):
if rw == 'r':
event_type = event.EV_READ
elif rw == 'w':
event_type = event.EV_WRITE
else:
assert False, "rw must be one of ['r', 'w']"
self._event = event.event(fd, event_type, self._on_event)
self._channel = Channel() #this is were wait will block on
self._current_callback = None

Создаётся событие event.event (через байндинг на pyx), оно привязывается
к libevent с флагом EV_READ, а потом создаётся канал self._channel, на
который можно подписаться и ждать оттуда пинков, когда можно забирать
данные.

Есть класс сокета (lib/concurrence/io/socket.py), в котором к сокету
привязываются такие обработчики событий:

def _get_readable(self):
if self._readable is None:
self._readable = FileDescriptorEvent(self.fd, 'r')
return self._readable

Вот это событие (self._readable) будет триггериться, когда в сокете
появятся данные.

Пример использования этого readable есть в том же socket.py:

self.readable.wait(timeout = timeout)
bytes_read, _ = buffer.recv(self.fd) #read from fd to

Вам надо сделать обработчик wait_select для драйвера базы, который
вместо select.select() будет вызывать self.readable.wait(). Как-то так.

Если реализуете, надеюсь на пулл реквест ;-)

Роман Кривенков

unread,
Dec 19, 2012, 6:52:43 AM12/19/12
to ru-pyth...@googlegroups.com
Написал. Нормально работало, пока не добавил таймауты :)

Проверяю на блокировании запросом SELECT pg_sleep(2). Таймаут выставлен в 1 сек.
Жутко тормозит на посылке Exception, рез-ты профилирования:

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
286   25.943    0.091   25.943    0.091 {method 'send_exception' of 'channel' objects}
91 мсек на вызов, это нереально много..

Запросы в сек соответственно падают с 270 до 12..

пятница, 14 декабря 2012 г., 12:23:18 UTC+4 пользователь Alexander Lourier написал:

Alexander Lourier

unread,
Dec 19, 2012, 12:06:27 PM12/19/12
to ru-pyth...@googlegroups.com
19.12.2012 15:52, Роман Кривенков wrote:

> Написал. Нормально работало, пока не добавил таймауты :)
>
> Проверяю на блокировании запросом SELECT pg_sleep(2). Таймаут выставлен
> в 1 сек.
> Жутко тормозит на посылке Exception, рез-ты профилирования:
>
> ncalls tottime percall cumtime percall filename:lineno(function)
> 286 25.943 0.091 25.943 0.091 {method 'send_exception' of
> 'channel' objects}
> 91 мсек на вызов, это нереально много..

Хм. 91 мс это собственное время send_exception или вместе с обработчиком
исключения? Можно код посмотреть и как тестировали?

Роман Кривенков

unread,
Dec 21, 2012, 1:39:31 AM12/21/12
to ru-pyth...@googlegroups.com
Извиняюсь, что долго не отвечал. Нашел в коде серьезные ошибки, отлаживал..
Сейчас работает стабильно, кроме неизвестных тормозов на method 'raise_exception' of 'tasklet' objects.

Весь код доступен в моей копии репозитария на GitHub, ветка psycopg2. Pull-request пока делать рано, еще потестирую..
Прикрепил тестовый сервер в файле main.py.
Результаты профайлинга:

Ordered by: internal time
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      288   24.730    0.086   24.730    0.086 {method 'raise_exception' of 'tasklet' objects}
      646    8.477    0.013    8.477    0.013 {concurrence._event.loop}
     1428    0.681    0.000    0.681    0.000 {method 'poll' of 'psycopg2._psycopg.connection' objects}
     1278    0.258    0.000    0.258    0.000 {stackless.schedule}
     1127    0.191    0.000    0.191    0.000 {method 'send' of 'channel' objects}
        1    0.015    0.015   34.379   34.379 core.py:761(_dispatch)
     1428    0.008    0.000    0.768    0.001 connection.py:46(_handle)
     1213    0.004    0.000    0.004    0.000 {method 'add' of 'concurrence._event.__event' objects}
     1127    0.003    0.000    0.195    0.000 core.py:697(send)
      889    0.002    0.000    0.122    0.000 core.py:89(_channel_callback)
     3272    0.002    0.000    0.002    0.000 {concurrence._event.has_next}
      889    0.002    0.000    0.124    0.000 core.py:81(_on_event)
      288    0.002    0.000   24.732    0.086 core.py:681(on_timeout)
      308    0.002    0.000   24.734    0.080 core.py:144(_on_event)
     2626    0.002    0.000    0.002    0.000 {concurrence._event.next}
     1365    0.001    0.000    0.001    0.000 core.py:643(balance)
     1924    0.001    0.000    0.001    0.000 {stackless.getruncount}
...

По неизвестным причинам tasklet.raise_exception начинает тормозить, это отражается и на рез-тах бенчмарка:
pc@note> siege -c500 -t10s http://localhost:8000                                                                                    ~
** SIEGE 2.70
** Preparing 500 concurrent users for battle.
The server is now under siege...
Lifting the server siege...      done.
Transactions:          70 hits
Availability:      100.00 %
Elapsed time:        9.38 secs
Data transferred:        0.00 MB
Response time:        6.53 secs
Transaction rate:        7.46 trans/sec
Throughput:        0.00 MB/sec
Concurrency:       48.73
Successful transactions:          70
Failed transactions:           0
Longest transaction:        7.55
Shortest transaction:        0.00

Тут должно-быть хотяб 50 запросов в секунду.

Аналогичная картина в отладочном выводе.
Возможно профилировщик тупит и показывает тормоза не там.
А возможно он принимает блокирование за тормоза.

Тест без драйвера работает быстро, см. test.py.
Буду разбираться дальше..

среда, 19 декабря 2012 г., 21:06:27 UTC+4 пользователь Alexander Lourier написал:
test.py
main.py

Alexander Lourier

unread,
Dec 21, 2012, 2:52:59 PM12/21/12
to ru-pyth...@googlegroups.com
21.12.2012 10:39, Роман Кривенков wrote:

> Извиняюсь, что долго не отвечал. Нашел в коде серьезные ошибки, отлаживал..
> Сейчас работает стабильно, кроме неизвестных тормозов на method
> 'raise_exception' of 'tasklet' objects.

Это может происходить в таком случае. Вы отправляете
Channel.send_exception(), а на другом конце канала его никто не слушает.
В результате, send_exception блокируется и ждёт receiver'а на другом конце.

Судя по вашему коду, везде проверки баланса есть. Но может какие-то
косвенные вызовы есть?

Попробуйте через профайлер посмотреть, что там в стеке на
raise_exception. Может быть, понятно будет что-то.

Роман Кривенков

unread,
Dec 24, 2012, 10:46:10 AM12/24/12
to ru-pyth...@googlegroups.com
Выяснил, что профилировщик нагло врет.
Метод connection.cancel оказался блокирующимся, но в профайлинге он даже не отображается.

Он передает по сети команду отмены запроса, причем на локалхосте выполняется ~20мсек, а по инету ~100мсек (зависит от инета).
Другого способа отменить запрос нет. Думаю пока оставить как есть, и написать разработчикам Psycopg2.

Причесываю пока код..

пятница, 21 декабря 2012 г., 23:52:59 UTC+4 пользователь Alexander Lourier написал:
Reply all
Reply to author
Forward
0 new messages