Измерение производительности популярных python web-фреймворков
Содержание:
На праздниках решил протестировать популярные python фреймворки для web на производительность. В отличии от других тестов не стал сосредотачиваться на деплойменте, тестируя разнообразные связки uwsgi/gunicorn/chaussette/waitress/nginx и тд. Но на тестировании именно самих платформ. В частности меня интересовала «плата за асинхронность» — насколько набирающие популярность асинхронные фреймворки превосходят/уступают синхронным WSGI-решениям.
Участники тестирования
Note
Описания взяты с оригиналов
- Aiohttp 0.16.3 -- Асинхронный фреймворк базирующийся на Asyncio;
- Bottle 0.12.8 -- Быстрый, простой и легковесный WSGI микрофреймвок;
- Django 1.8.2 -- Веб-фреймворк для перфекционистов с горящими сроками;
- Falcon 0.3.0 -- Высоко-производительный фреймворк для построения облачных API;
- Flask 0.10.1 -- Микрофреймворк базирующийся на Werkzeug, Jinja2 и хороших намерениях;
- Muffin 0.0.88 -- Асинхронный фреймворк базирующийся на Asyncio и Aiohttp;
- Pyramid 1.5.7 -- Небольшой, быстрый и понятный веб-фреймворк;
- Tornado 4.2 -- Асинхронная сетевая библиотека и веб-фреймворк;
Методика тестирования
Тесты прогонялись на Amazon EC2 t2.medium сервере. Для создания нагрузки использовалась утилита WRK запущенная на том же сервере с параметрами:
wrk -d30s -t12 -c400 [URL]
Все приложения (кроме Tornado) запускались при помощи Gunicorn (2 процесса на каждое). Для синхронных WSGI библиотек использовался Meinheld worker.
Приложение на Tornado запускало 2 процесса, используя средства самого фреймворка.
Все тесты производились с использованием Python 3.4.
Каждое приложение тестировалось по трем основным сценариям:
- JSON-тест -- закодировать небольшой объект в JSON и вернуть клиенту.
- Remote-тест -- требуется загрузить ответ от другого сервера и вернуть его клиенту.
- Complete-тест -- Используя ORM загрузить коллекцию объектов из базы, добавить к ней еще один и отрендерить список в шаблоне.
Первый сценарий это своеобразный «Hello World!» лишь несколько усложненный процессом кодирования в JSON, что практически не влияет на результаты. Показывает именно быстродействие в плане обработке HTTP-запросов.
Второй сценарий несколько синтетический, тк его результаты весьма предсказуемы. Тем неменее он должен показывать насколько хорошо платформа справляется с длительными операциями ожидания во время обработки запроса.
Третий сценарий предпалагается как комплексный тест и имитирует реальную жизнь, а именно использование базы данных, ORM, движка шаблонов.
В качестве базы данных использовался Postgresql с дефолтными настройками.
Исходники приложений можно найти на Github.
Результаты
Name | 50% (ms) | 75% (ms) | Avg (ms) | Req/s | Non 200-x | Timeouts |
---|---|---|---|---|---|---|
Aiohttp | 91.67 | 103.1 | 108.01 | 4093.41 | ||
Bottle | 24.77 | 26.23 | 25.06 | 15761.45 | ||
Django | 103.2 | 112.19 | 103.36 | 3696.90 | ||
Falcon | 19.24 | 19.81 | 19.19 | 20677.13 | ||
Flask | 64.32 | 71.59 | 65.68 | 6023.40 | ||
Muffin | 108.07 | 115.09 | 171.56 | 3575.36 | ||
Pyramid | 41.75 | 43.49 | 41.63 | 9402.69 | ||
Tornado | 138.24 | 149.84 | 136.87 | 2829.72 |
В первом простом тесте с хорошим отрывом победили синхронные фреймворки. Не считая Django, но в оправдание последнего, можно сказать, что по-умолчанию этот фреймворк делает множество работы (middlewares by default). Асинхронные фреймворки делят места аутсайдеров и неожиданно для меня на последнем месте оказался Tornado. Очень хорошие результаты у Falcon и Bottle.
Name | 50% (ms) | 75% (ms) | Avg (ms) | Req/s | Non 200-x | Timeouts |
---|---|---|---|---|---|---|
Aiohttp | 358.08 | 369.08 | 338.94 | 1120.27 | ||
Bottle | 3363.74 | 9911.84 | 6403.92 | 19.09 | 335 | |
Django | 3317.64 | 12954.23 | 6918.64 | 18.96 | 300 | |
Falcon | 3196.23 | 12976.84 | 6696.17 | 19.28 | 328 | |
Flask | 3306.88 | 11690.8 | 6824.88 | 19.16 | 363 | |
Muffin | 372.95 | 428.75 | 376.98 | 1019.76 | ||
Pyramid | 3295.1 | 10518.92 | 6673.78 | 19.35 | 338 | |
Tornado | 1994.39 | 2069.25 | 1928.31 | 194.37 |
Для понимания результатов следующего теста необходимо пояснить, что приложения обращались к nginx настроенному на ответ с задержкой 100ms. Из-за этого результаты синхронных фреймворков очень близки, практически вся их работа после определенного момента сводилась к ожиданию. Опять неожиданные результаты от Tornado, я предполагал, что он будет близок к Aiohttp и Muffin. Но тем неменее Tornado в 10 раз эффективнее в этом кейсе чем ближайший синхронный фреймворк.
Name | 50% (ms) | 75% (ms) | Avg (ms) | Req/s | Non 2xx | Timeouts |
---|---|---|---|---|---|---|
Aiohttp | 151.78 | 156.9 | 254.75 | 1004.82 | 68% | 236 |
Bottle | 613.5 | 630.17 | 1062.86 | 451.34 | 178 | |
Django | 1610.46 | 1976.44 | 2632.36 | 88.57 | 42 | |
Falcon | 766.75 | 805.35 | 1457.99 | 350.26 | 81 | |
Flask | 1032.63 | 1649.89 | 1465.25 | 222.78 | 496 | |
Muffin | 420.14 | 485.4 | 1552.7 | 819.62 | ||
Pyramid | 562.44 | 601.49 | 812.43 | 248.42 | 235 | |
Tornado | 937.37 | 988.86 | 910.06 | 418.36 |
И последний тест. Результаты Aiohttp можно игнорировать тк к сожалению более двух третей запросов вернули 502 ошибки. На первом месте неожиданно, но приятно, оказался мой фреймворк Muffin достаточно быстро обрабатывающий этот тест. Django значительно проигрывает, лишь подтверждая медлительность стандартного движка шаблонов и ORM.
Выводы
Я предлагаю читателю сделать их самостоятельно. Данное тестирование производилось мой чтобы понять выгоду использования синхронных/асинхронных библиотек и показатели производительности популярных решений.
В дальнейшем планирую проводить данные измерения регулярно.