Ещё раз о производительности фреймворков Python для веб разработки

    Недавно мне пришлось начинать проект нового веб сервиса, и я решил протестировать максимальную нагрузочную способность Django, а заодно сравнить её с Flask’ом и AIOHTTP. Результат показался мне неожиданным, поэтому я «просто оставлю» его тут.

    На диаграммах ниже приведены результаты простейшего Apache Benchmark’a для фреймворков Django версии 3.1, Flask 1.1 и AIOHTTP 3.7. AIOHTTP работает в «штатном» однопоточном асинхронном режиме, Django и Flask обслуживаются синхронным WSGI сервером Gunicorn с числом потоков, равным числу доступных ядер процессора * 2. ASGI в тесте не участвовал.

    Условия тестирования
    Во всех трёх случаях выводится простая страница со списком по результатам выборки из реляционной базы данных PostgreSQL. Запрос я постарался сделать максимально приближенным к реальности:

    SELECT r.id, r.auth_user_id, r.status, r.updated, r.label, r.content, u.username,
        ARRAY_AGG(t.tag) tag, COUNT(*) OVER() cnt,
        (
            SELECT COUNT(*) FROM record r2
                WHERE
                    r2.parent_id IS NOT NULL
                    AND r2.parent_id = r.id
                    AND r2.status = 'new'
        ) AS parts
    FROM record r
    JOIN auth_user u ON u.id = r.auth_user_id
    LEFT JOIN tag t ON t.kind_id = r.id AND t.kind = 'rec'
    WHERE r.parent_id IS NULL AND r.status = 'new'
    GROUP BY r.id, u.username
    ORDER BY r.updated DESC
    LIMIT 10 OFFSET 0
    

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

    AIOHTTP использует пулл соединений с БД и драйвер asyncpg, Django и Flask — SQLAlchemy без ORM (для чистоты эксперимента) и psycopg2.

    Приложение Django создано стандартными средствами фреймворка (django-admin startproject, manage.py startapp и т. д.), вывод тестовой страницы через ListView. Установки Flask и AIOHTTP построены на канонических веб приложениях «Hello, world», взятых из документации.

    Результаты запуска теста на локальной машине (4 ядра CPU):



    UPD: как справедливо пишут в комментариях, для честного сравнения следовало либо запустить AIOHTTP за Gunicorn'ом, либо уменьшить число воркеров до 1.

    Тот же тест на реальном однопроцессорном VDS (пинг около 45 ms):



    Во время теста AIOHTTP использовал 100% одного ядра CPU, Flask и Django — 100% всех доступных ядер.

    Выводы


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

    А вот скромный результат Flask’а с трудом поддается объяснению, «разогнать» этот фреймворк у меня не получилось.

    Средняя зарплата в IT

    113 000 ₽/мес.
    Средняя зарплата по всем IT-специализациям на основании 10 037 анкет, за 2-ое пол. 2020 года Узнать свою зарплату
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 15

      +2

      Где исходники? Какой драйвер PostgreSQL использовался? С какими аргументами вызывался ab? Почему у Gunicorn не выставили количество воркеров в 1 для честного сравнения?

        0
        Какой драйвер PostgreSQL использовался?

        Указано под спойлером, asyncpg и psycopg2.

        С какими аргументами вызывался ab?

        ab -n 10000 -c 100 host:port/

        количество воркеров в 1

        Задача стояла замерить только максимальную производительность. По-хорошему, нужно было бы честно строить графики зависимости от числа одновременных соединений, воркеров, загрузки процессора и т.д. Но это уже тянет на серьёзную статью и требует врмени. А так — вы правы, с одим воркером асинхронное приложение выигрывает по производительности.
          +1
          ab -n 10000 -c 100 host:port/

          Попробуйте добавить -k. К слову сказать, asyncpg должен заметно уделывать psycopg2 по производительности

            0
            Попробовал.
            KeepAlive никак не повлиял на Gunicorn, а вот число запросов AIOHTTP подросло процентов на 25 локально, спасибо за информацию.
        +1
        Запрос у вас жутковатый, интересно прям explain его глянуть.
        Колоночный подзапрос я бы на join переложил с window function или группировкой
        Хотя я бы число прямых потомком денормализовал, перевычислять его каждый раз при выполнении select — жуть.
          0
          Запрос взят из первого попавшегося проекта, мне просто было важно, чтобы он был, иначе цифры бенчмарка зашкаливают. Подзапросы я тоже не очень люблю, если руки дойдут — оптимизирую обязательно, спасибо.
          +1
          На самом деле, сравнение асинхронных и многопоточных приложений не совсем корректно — они решают разные задачи.

          В данном случае приложения решают одну и ту же задачу. Как Apache и nginx — вполне правомерно сравнивать на задачах типа отдачи статики

            +1

            Вы можете с помощь gunicorn запустить aiohttp на всех ядрах процессора. Раздел Server Deployment в документации aiohttp.

              0
              Я знаю про возможность параллельного запуска асинхронных приложений. Скорее всего, я зря включил первую диаграмму в эту заметку, сравнение со standalone сервером, действительно, некорректно.

              На самом деле меня удивили показатели Django, имеющего репутацию «медленного» фреймворка, эта заметка о нем.
              0
              есть же сравнение (почти) всех фреймворков www.techempower.com/benchmarks
                0
                У джанго ужасно тормозной шаблонизатор =) Фикситься это заменой шаблонизатора
                  +1
                  Какие интересные результаты. Но прямо порадовали, значит можно и дальше пользоваться Джанго и не переживать, спасибо!

                  Еще бы тут Нода была, было бы вообще замечательно… хотя это уже не Питон.
                    0

                    FastAPI?

                      0
                      Выше RekGRpth дает ссылку на сравнение огромного числа фреймворков. Там, правда, не только про питон.

                      У меня не хватило времени даже на сравнение со встроенным в Django (начиная с 3.0) ASGI, тут просто путевая заметка, которая как бы намекает, что вещи иногда могут быть не такими, какими кажутся, или какими их принято считать :)
                        0

                        Однозначно. Учитывая что код остается практически тем-же

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое