Сделал "пузомерку" для сравнения производительности 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% ускорение.
Если убрать queue то результат для aiozmq улучшится.
ОтветитьУдалитьАндрей, спасибо за проделанную работу!
ОтветитьУдалитьМогли бы вы добавить описание машины и ОС на которой производили тестирование?
И, если возможно, добавьте возможность запуска отдельно сервера и отдельно клиента для каждого теста, чтобы была возможность погонять на физически разных машинах в сети и возможность выбора протокола (tcp://, ipc://, inproc://).
Олег, я не понял о какой queue идет речь.
ОтветитьУдалитьВ тесте "core aiozmq" используется queue, а в "zmq" нет. Он дает оверхед.
ОтветитьУдалитьВот вариант без queue: https://gist.github.com/anonymous/10650805
В результате "core aiozmq" вырос от 4600rps до 6100rps против 5600rps для zmq (single/threaded).
Т.е. queue "съел" 25% производитльности.
Так же заметил, что при этом варианте, результаты для pyzmq немного проседают.
Спасибо, поправил
ОтветитьУдалитьKentzo: машинка самая обычная. На разных машинах меняются конкретные цифры но не их соотношение.
ОтветитьУдалитьДругие протоколы делать не очень-то хочу прямо сейчас. С другой стороны если будет pull request -- вклею с удовольствием.
Раздельный клиент и сервер мелать хочется еще меньше. Просто потому что, как мне кажется, получившимся инструменом будет не очень удобно пользоваться. Опять же если будет толковый pull request....