Pull to refresh

Comments 44

При этом выбран один процесс Uvicorn, без Gunicorn и множества воркеров, чтобы условия для обоих серверов были сопоставимыми. 

А сколько был GOMAXPROCS? Какую нагрузку на CPU давал каждый сервер?

Запуск Go на одно ядро GOMAXPROCS(1) сделает условия более сопоставимыми с Python, но даже так Go останется заметно быстрее, просто разница будет не столь существенной.

Как и обычно, в таких "тестах", ничего общего с практикой у бенчмарков. В этом нет никакого смысла — статья, ради статьи. Тем более, что уже давно есть ресурс https://www.techempower.com/benchmarks/, где бенчмарки не пустые.

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

Это упрощенный эксперимент для иллюстрации разницы в производительности чистого HTTP стека go и python. Цель показать цифры под контролируемой нагрузкой, а не соревноваться с комплексными бенчмарками вроде techempower, где учитываются базы данных, кэши, разные фреймворки и реальные сценарии

Бенчмарк с помощью инструмента, написанного на чистом Python? Вы там лол совсем?

кто-то сомневалмя что хомячка съедят

Технически Locust на Python может упираться в CPU и память, но при 1000 пользователей он работает эффективно, и тормоза одинаково сказываются на тестах Go и Python, так что сравнение остается корректным.

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

Ну если весы будут показывать, что гиря весит 0.7кг, а перо весит 0.4кг, то у меня к таким весам определённо будут вопросы)

Почему выбран го 1.25.5 (выпущен 3 недели назад), но питон двухлетней давности?

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

В смысле, вы предлагаете взять го ещё посвежее? Не бывает различий языков, бывают различия реализаций - собственно, вы и компилятор питона не указали. Чистый питон на pypi, например, был бы на порядок шустрее cpython. А в (с)питоне 3.14 навалили гору оптимизаций, которую вы почему-то решили проигнорировать.

Было бы интересно сравнить с производительностью чистого nginx на той же машине

Да, это было бы интересно. Nginx оптимизирован для максимально эффективной работы с сетью и потоками. Но опять же повторюсь, что серверы в статье - для измерения runtime и модели конкуренции

Немного странно, что питон так несправедливо занизили. Было бы интересно увидеть: последнюю версию без gil + стандартная библиотека + async + возвращать также байты. А ещё можно добавить компиляцию или оптимизированную сборку питона.

Без gil было бы медленнее, это же однопоточный код, из-за асинка

Точно, тут gil почти не влияет. async/await в python не создаёт новых потоков для каждого запроса - все запросы обрабатываются в одном потоке, переключаясь между задачами. Так что отключение gil ничего не ускорит, а может даже чуть замедлить за счёт накладных расходов на управление потоками.

Такой тест дал бы более «максимальный» результат для Python, но тогда уже сравнивать чистый язык с Go стало бы сложнее

Что странно? Даже если взять последнюю версию Пайтон, включить оптимизированную сборку, использовать асинк с возвратом байтов, то это помогло бы лишь частично догнать го по RPS. Тут очевидно, что чистая конкурентность и использование всех ядер всё равно будут сильнее в пользу гошки, потому что его рантайм изначально спроектирован под высокую многопоточность

Небось Go расползся по всем ядрам, а Питон грел только одно...

Наверняка. Но тогда непонятно почему разница такая маленькая.

Вот этот Locust, как он, интересно, оценивает предел нагрузочной способности? Раз отказов не было, он как-то понимает, в какой момент надо остановиться и перестать повышать нагрузку. Интересно, по каким критериям он это делает?

И другой вопрос. Посылать и принимать HTTP-запросы - работа сравнимая. Locust ведь тоже мог упереться в пределы своей производительности. Благо что он написан на Питоне, исполняется на той же машине и тоже, вероятно, в одно ядро. Этот вариант в процессе тестирования не исключен.

В общем, к проведённому исследованию возникает много методологических вопросов...

Locust просто шлет запросы согласно заданным параметрам и собирает статистику. Если отказов нет, он не останавливается, а просто фиксирует, сколько запросов удалось обработать за время теста. Да, Locust может упереться в свои пределы (CPU/IO), особенно на Python и одном ядре, и это влияет на результаты, но в нашем сценарии мы использовали одинаковые условия для обоих серверов, так что сравнение, очевидно, остается корректным для относительной производительности.

суть - показать различия в runtime и модели конкурентности.

Очень странное сравнение:

  • Почему-то в качестве тестируемой OS выбран Windows. Я очень давно не видел Windows на серверах, а от операционной системы зависит очень много;

  • Тестирующий софт запущен на той же машине. При этом он может требовать ресурсов больше, чем тестируемое приложение;

  • Go-приложение по-умолчанию будет пытаться утилизировать все ядра, про Python я такого сказать не могу.

Да, на Windows результат будет немного отличаться, особенно для Go, но и цель статьи показать относительную разницу в чистом бенчмарке, а не дать цифры для продакшен-сервера.

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

Выбор net/http вместо популярных фреймворков вроде Gin или Echo обусловлен именно этим: эксперимент должен показать возможности языка, а не фреймворка.

Для Python был выбран FastAPI вместе с Uvicorn. FastAPI - современный web-фреймворк

Ну справедливое сравнение, что уж :) Тогда и для Go брали бы какой-нибудь fasthttp, что ли.

Сравнение корректно в рамках поставленной задачи. Go - стандартная библиотека и дефолтный runtime, с fasthttp Go был бы быстрее, Python - самый производительный, но типичный продакшен-стек.

Для реального теста трафик лучше слать не loopback (localhost), а реально в другой машины. Так, у этого теста не очень много с реальностью. Ну да, Go в целом быстрее, чем питон.

Мы с коллегами однажды nodejs и Go сравнивали - примерно также, как в этой статье. На loopback Go был значительно быстрее. Но с реальным трафиком разница не была такой высокой и большинство выгод от Go терялись с сети.

Справедливое замечание, и я с ним в целом согласен.

Loopback-тест действительно убирает сетевые задержки и смещает фокус на производительность runtime и HTTP-стека. В этом и была цель эксперимента - посмотреть поведение серверов под высокой конкурентной нагрузкой, без влияния сети.

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

В реальных системах обычно присутствуют оба фактора: и сетевые задержки, и нагрузка на сервер. Поэтому loopback тест не претендует на полную модель, но хорошо показывает пределы и особенности runtime под нагрузкой.

Отдельно стоит отметить, что даже если сеть нивелирует часть разницы, стабильность на верхних процентилях (98–99%) всё равно остаётся важной, особенно при росте нагрузки.

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

Можно ли докрутить такие тесты , чтобы процесс зашел в OOM? Интересно было бы посмотреть , кто будет первый .

да, можно. Python, как правило, упирается в память раньше из-за большего memory footprint на запрос. Go в таких условиях держится дольше.

Вот что-что, а память Питон жрёт как удав. Просто до осени 2025 это вообще не было проблемой.

В описанном случае GIL никак не влияет на скорость ответа, так как запускается один поток, а вот Go может их плодить. Теперь вопрос: насколько справедливо сравнение?

сравнение справедливое в рамках заявленных условий. Go плодит потоки не потому что мы ему помогли, а потому что так устроен runtime.Python требует явного масштабирования по процессам и это уже другая архитектура, потребление памяти, операционные компромиссы.

Вопрос: Почему верхние percentiles у FastAPI такие высокие (например, 98-й — 2400 мс)?Ответ: В Python есть GIL

Вот это можно поправить в статье, в данном контексте неверно.

И все таки интересно было бы посмотреть на более равные условия при runtime.GOMAXPROCS(1)

Да, это стоит уточнить. В текущей статье сравнение идёт из коробки. Но если честно, думаю, что запускать Go только на одно ядро не сильно повлияет. Это уже придирка, Go все равно останется выше, хоть и не в таких экстремальных пропорциях, как при использовании всех ядер.

Эластичность не проверили, циклическая нагрузка нужна.

Ну и 30 сек экстремально мало. Я провожу нагрузочные тесты 5..6..7 часов и часто проседание ловится только по истечении 3..4 часов. А тут получился этакий тест дидос фильтра, который должен сработать при обнаружении атаки на уровне приложения и дать команду прокси серверу начать защищаться.

По результатам тестов какие-то запросы выполнялись аж 21 секунду. В обоих случаях. И весьма интересно, что именно их настолько задержало. Причём нельзя исключить, что проблемы производительности стали возникать на уровне клиента.

Так что даже сложно сказать, что тестировалось - ПО сервера, клиента или самой ОС по распределению нагрузки между ними.

Кто то мне разжует о каких практичечких выводах может идти реч в тестах hello world?

Всё хорошо, но чтобы результаты были статистически значимыми, лучше было бы провести этот тест несколько раз, чтобы исключить выбросы или аномалии.

Фраза FastAPI достаточно быстр для большинства задач теперь имеет цифры. Было интересно узнать, для каких задач FastAPI справится с запасом, а для чего - однозначно Go.

Почему никто не заметил, что сервера возвращают разные по длине ответы?

Go-Server 9 байт

Python-Server 13 байт, на 44% больше!

Sign up to leave a comment.

Articles