понедельник, 28 апреля 2014 г.

DevConf 2014

14 июня будет ежегодная конференция разработчиков DevConf 2014.

Там есть и питоновская секция.

Посещал конфу последние четыре года, собираюсь и в этот раз.

Если хотите послушать доклады и пообщаться с коллегами -- покупайте билеты.

Желающим выступить -- заявки на доклады принимаются еще две недели.

понедельник, 14 апреля 2014 г.

aiozmq benchmark -- обновление

Вчера написал измеритель производительности aiozmq.

Аж пять тестов, три для pyzmq и два для aiozmq.

Сегодня захотел посмотреть, насколько правильные цифры они показывают.

Для оценки результатов измерений давным-давно была изобретена математическая статистика. Имея выборку (результаты нескольких замеров) вычисляем среднеквадратическое отклонение и доверительный 95% интервал. Последний более понятен для интерпретации: если на пальцах то это тот диапазон, в который попадает наша величина с вероятностью 95%.

Дело в том что результаты измерений всегда дают некоторый разброс, и нужно оценить насколько этот разброс велик (можно ли верить полученному среднему значению).

В numpy / scipy уже всё есть, математику вспоминать не нужно.

Пример для вычисления request per second для списка data, каждый элемент которого содержит время выполнения count запросов RPC.

from scipy.stats import norm, tmean, tvar
from numpy import array

rps = count / array(data)
rps_mean = tmean(rps)
rps_var = tvar(rps)
low, high = norm.interval(0.95, loc=rps_mean, scale=rps_var**0.5)

Анализ

И что я увидел? Тесты для aiozmq давали просто чудовищный разброс: среднеквадратическое отклонение зашкаливало, а нижняя граница доверительного интервала иногда уходила в минуса.

Т.е. моим тестам просто нельзя верить!!!

Несколько раз перепроверив убедился, что проблема именно в тестах а не в неправильной обработке статистики.

Начал рабираться и довольно быстро обнаружил источник беды: я не освобождал все ресурсы после запуска теста (не закрывал транспорты и сервисы aiozmq). В результате при каждом следующем запуске тестового кода asyncio event loop был вынужден делать чуть больше работы. Совсем чуть-чуть, но ведь мы измеряем десятые доли милисекунды и разница начинает быть заметной.

Быстро поправил и жизнь наладилась.

Плюс Олег Нечаев заметил, что тест для aiozmq core не вполне корректен и занимает на 25% больше времени чем должен.

Первоначальные 3-5 запусков каждого теста были явно недостаточны для сбора статистики. Из институтских лаб помню, что выборка в которой меньше 30 измерений -- и не выборка уже а сплошное недоразумение. Увеличил количество до 100.

Чтобы тесты показывали более стабильные результаты увеличил до 5000 количество request-reply для каждого теста.

Время работы измерялки стало напрягать. Добавил запуск тестов на нескольких процессах.

У меня четыре виртуальных ядра, так что время уменьшилось в ожидаемые 4 раза.

Обновленные тесты:

(aiozmq)andrew@tiktaalik-3:~/projects/aiozmq (master)$ python benchmarks/simple.py -n 5000 -t 100
Run tests for 100*5000 iterations: ['aiozmq.rpc', 'core aiozmq', 'single thread raw zmq', 'single thread zmq with poller', 'zmq with threads']
....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................

RPS calculated as 95% confidence interval
Results for aiozmq.rpc
RPS: 3797: [3589, 4005],    mean: 263.533 μs,   standard deviation 7.252 μs

Results for core aiozmq
RPS: 10875: [9806, 11944],  mean: 92.190 μs,    standard deviation 4.842 μs

Results for single thread raw zmq
RPS: 20622: [14559, 26684], mean: 49.840 μs,    standard deviation 9.238 μs

Results for single thread zmq with poller
RPS: 18020: [16687, 19353], mean: 55.572 μs,    standard deviation 2.147 μs

Results for zmq with threads
RPS: 19550: [14768, 24331], mean: 52.101 μs,    standard deviation 7.761 μs

Машинка другая, десктопный Intel(R) Core(TM) i5-3470 CPU @ 3.20GHz, 8GB, 2(4) cores(hyperthreads).

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

Домашний ноут, кстати, теперь показывает совсем скромные результаты в multiprocessing (а вроде бы те же 4 ядра, но он быстро перегревается и уходит в throttling:

(aiozmq)andrew@tiktaalik2:~/projects/aiozmq (master)$ python benchmarks/simple.py -n 5000 -t 100
 Run tests for 100*5000 iterations: ['aiozmq.rpc', 'core aiozmq', 'single thread raw zmq', 'single thread zmq with poller', 'zmq with threads']

....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................

RPS calculated as 95% confidence interval
Results for aiozmq.rpc
RPS: 975: [747, 1204],  mean: 1038.325 μs,  standard deviation 115.027 μs

Results for core aiozmq
RPS: 3136: [2176, 4097],    mean: 326.753 μs,   standard deviation 52.423 μs

Results for single thread raw zmq
RPS: 6429: [4138, 8721],    mean: 161.251 μs,   standard deviation 32.622 μs

Results for single thread zmq with poller
RPS: 5461: [3879, 7042],    mean: 187.659 μs,   standard deviation 31.816 μs

Results for zmq with threads
RPS: 6349: [4730, 7967],    mean: 160.318 μs,   standard deviation 22.218 μs

Мораль

Сравнивайте относительные показатели -- современные процессоры слишком сильно отличаются друг от друга. Ноутбук не допускает перегрева, десктоп к нагреванию относится помягче а сервер живет в раскалённом состоянии. Виртуальные сервера на Амазоне -- отдельная тема.

Если делаете benchmarks -- проверьте результаты статистически.

Распространенный в интернете способ: "запустить три раза и выбрать лучший результат" никуда не годится.

Средний результат тоже ни о чём не говорит.

Нужно запустить тест как минимум 33 (а лучше 333) раза и оценить не только среднее время а и среднеквадратичное отклонение как минимум.

Я -- далёкий от математики человек. Как и подавляющее большинство программистов, мне кажется. Изучал в институте, да с тех пор прошло 15 лет и почти всё выветрилось.

Среднеквадратичное отклонение мне мало о чём говорит. Много или мало -- сложно было сказать сразу после того как я начал собирать статистику для своих тестов.

Но я вспомнил что на моём любимом сайте http://elementy.ru в научных статьях часто используют схемки вроде этой:

Доверительный
интервал.

К сожалению сайт элементов стал недоступен из Киева по неизвестной мне причине несколько недель назад. Читаю его через буржуйский VPN. Поэтому буду рассказывать что к чему на примере из википедии.

Столбики -- это средние значения. А красные вертикальные линии на столбиках -- это именно доверительные интервалы, заданные с какой-то погрешностью. Обычно принято использовать 95% если явно не указано другое значение.

По этим красным вертикальным линиям очень наглядно видно, насколько полученное среднее значение измеряемой величины размазано по выборке.

Надеюсь, все поняли основную идею: смотрим на результат теста и обращаем внимание на границы изменчивости измеряемой величины.

P.S.

Мне бы тоже стоило выложить похожую картинку, но я не знаю matplotlib и не могу сделать красивый график.

Помогите, пожалуйста.

Лучший вариант -- pull request на github который позволит запустить python benchmarks/simple.py -n 5000 -t 100 --save-plot=file.png или что-то вроде того.

Upd

Спасибо Артему Дудареву, теперь у меня есть хорошая генерилка графиков:

картинка

воскресенье, 13 апреля 2014 г.

Оценка производительности aiozmq

Сделал "пузомерку" для сравнения производительности aiozmq и просто pyzmq.

aiozmq использует pyzmq в своих внутренностях и стало интересно узнать, какие тормоза добавляет связка aiozmq + asyncio по сравнению с "простыми zmq сокетами".

Тест делался для пары DEALER/ROUTER (RPC) в разных режимах.

Результаты запуска измерителя производительности:

(aiozmq)andrew@tiktaalik2:~/projects/aiozmq (master)$ python benchmarks/simple.py -n 10000
Run tests for 10*10000 iterations: ['aiozmq.rpc', 'core aiozmq', 'single thread raw zmq', 'single thread zmq with poller', 'zmq with threads']
..................................................

Results for aiozmq.rpc
RPS: 2469,   average: 0.405 ms

Results for core aiozmq
RPS: 5064,   average: 0.197 ms

Results for single thread raw zmq
RPS: 9895,   average: 0.101 ms

Results for single thread zmq with poller
RPS: 12574,  average: 0.080 ms

Results for zmq with threads
RPS: 9702,   average: 0.103 ms

zmq шустрее, естественно. Обработка request-response на zmq в одном потоке примерно вдвое быстрее той же работы, которую делает aiozmq на своих транспортах и протоколах, плюс еще asyncio добавляет тормозов.

Даже на нитях (threads) zmq уверенно побеждает. В этом заслуга libzmq, которая создает свой внутренний thread для обработки send и в результате для Питона send получается неблокирующим.

aiozmq.rpc добавляет тормозов по сравнению с aiozmq.core примерно в два раза. Я считаю это приемлемой платой за прозрачную упаковку/распаковку аргументов вызываемой функции, поиск обработчика на стороне сервера, проверку сигнатур для параметров, пробрасывания исключения назад вызывающей стороне.

Если всю эту необходимую работу сделать на zmq -- думаю, получится не сильно быстрее.

Результат

aiozmq.core дает примерно 5000 requests per second, что довольно неплохо.

aiozmq.rpc способен выжать примерно 2500 rps.

То есть если вас устраивает обработка запроса к aiozmq.rpc меньше чем за одни милисекунду -- aiozmq вам подойдёт.

И, самое главное: если на стороне RPC сервера вы делаете запросы в redis, mongo, postgresql, mysql или обращаетесь каким другим внешним для вашего процесса ресурсам -- скорее всего тормоза будут именно в этом месте.

Почему это не очень важно

Да, я знаю что redis неимоверно быстр: показывает 70000+ rps на простых запросах. Но скорее всего вам таких обращений потребуется несколько, и делать вы их будете из питона используя библиотеку вроде asyncio-redis.

Которая добавляет немало приятных плюшек и расплачивается за это производительностью.

Это не значит что за скорость не нужно бороться. Просто для меня aiozmq показала ожидаемые и вполне неплохие результаты. Самый простой путь к ускорению лежит в оптимизации asyncio путём создания optional C Extensions для event loop и selector. Возможно, я этим займусь, или сделают другие Python Core Developers. Как это произошло с модулем io из стандартной библиотеки: после того как его переписали на С в Python 3.2 получили 30% ускорение.

понедельник, 7 апреля 2014 г.

PyCon RU 2014

Приглашаю всех на PyCon RU 2014.

В прошлом году году конференция прошла просто замечательно!
Интересные доклады и приятное место.

Как и в прошлом году конференция будет в загородном учебном центре Иволга (30 км от Екатеринбурга). Зимой там классно. Уверен, летом не хуже.

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

Не смотря на то что это аж в Екатиренбурге (далеко от Москвы и близко к географическому центру России) -- рекомендую посетить. О том, как было здорово в прошлый раз повторяться не стану.

Если у кого зудит в ягодицах от желания поделиться новым опытом -- заполните  форму на рассмотрение вашего доклада.

Я тоже приеду с лекцией об asyncio, моём опыте по участии в создании этой системы и написании библиотек для.

Может, будет еще один доклад, пока совершенно секретный (жду согласования).


воскресенье, 6 апреля 2014 г.

aiopg -- asyncio библиотека для PostgreSQL

Сделал библиотеку, которая может работать с PostgreSQL под asyncio.

Внутри используется psycopg2 в асинхронном режиме.

Есть connection pool.

Документация -- здесь.

суббота, 5 апреля 2014 г.

asyncio и HTTP

asyncio не умеет работать с HTTP.

Так и было задумано.

asyncio никогда не станет веб-сервером. Он делался как именно event loop для tcp, ssl, unix sockets, pipes и subprocesses. Плюс streaming API.

Веб был сознательно выпилен и теперь то что было лежит в aiohttp. Эта часть просто не дозрела до включения в стандартную библиотеку.

Идея такая:

  • WSGI -- синхронный протокол, для asyncio не подходит.
  • Какой будет новый стандарт -- неясно никому.
  • Пусть для asyncio люди попытаются сделать свои http либы и время покажет у кого получилось лучше.
  • Тогда, возможно, и появится новый стандарт.

Что касается меня то я пытаюсь понять какой именно должен быть API для HTTP server, что там должно быть обязательно и что нужно сознательно исключить.

Сейчас делаем это aiorest

Когда поймём, что получилось хорошо в aiorest -- займемся перенесением удачных решений в aiohttp. Там HTTP server слишком уж неудобный. А нужно что-то типа tornado.web, но более симпатичное и приятное.

четверг, 3 апреля 2014 г.

aiozmq -- поддержка ZeroMQ сокетов в asyncio

Наверное, уже все слышали про asyncio -- новую стандартную библиотеку для асинхронного сетевого программирования.

Естественно, asyncio не умеет работать с ZeroMQ сокетами и никогда не будет уметь.

На днях я выпустил первую версию библиотеки aiozmq, которая устраняет проблему.

aiozmq предоставляет 0MQ event loop совместимый с asyncio и высокоуровневые средства для организации вызовов удалённых процедур aka RPC.

Если интересны подробности -- читайте документацию, она довольно большая и подробная (постарался).