Перевод статьи Vadym Zakovinko “Golang vs. Python: Comparing Performance and Benchmarks”.
Здравствуйте, меня зовут Вадим, и это моя история о том, как я начал изучать Go, каково это по сравнению с Python (языком, который я сейчас использую на работе), и о бенчмаркинге.
Скачивайте книги ТОЛЬКО на русском языке у нас в телеграм канале: PythonBooksRU
Я считаю, что каждый разработчик должен постоянно учиться, чтобы быть хорошим специалистом в своем деле. И дело не только в знании новых фреймворков, баз данных или платформ, таких как AWS Lambda. Речь идет о знании того, как сервисы, которые вы используете изо дня в день, взаимодействуют друг с другом, о понимании того, когда ваш любимый язык использует ссылку на объект или копию объекта, и о многом другом.
Я использую Python в качестве основного языка для написания производственного кода уже 10 лет. Но даже будучи преданным разработчиком Python, я пробовал разные языки. Мне нравится изучать новые языки, потому что я могу из этого почерпнуть что-то для своей повседневной работы – даже из тех языков, которые я не буду использовать или из языков, которые мне не очень нравятся, например, Ruby или Java. Кроме того, для меня не очень важно, новый ли это язык. Мне нравятся и старые, редко используемые языки. Например, я пробовал Lisp для чего-то большего, чем написание конфигурации для Emacs. Мне также очень нравится Erlang, но он не слишком популярен в наши дни.
Знакомство с Go
Несколько недель назад я начал искать новый язык для изучения. Прежде всего, я начал изучать Elixir, поскольку мне нравится Erlang, но после прочтения учебника он мне не очень приглянулся. Через несколько дней я остановился на Go, так как много слышал об этом языке из разных источников. На первый взгляд, язык не казался сложным, но он требовал иного подхода к архитектуре приложений, чем, например, Python. Поэтому я решил изучить его поглубже.
Шаг первый
Сначала я погрузился в сообщество Go. В сообществе Go есть замечательный учебник, из которого вы почерпнете многие сведения о языке. Я считаю, что это отличная отправная точка: www.tour.golang.org. Учебник состоит из трех разделов, плюс раздел приветствия:
- “Basic” показывает основы синтаксиса Go, работу с переменными и функциями, операторы управления потоком и более сложные типы.
- Раздел “Methods and Interfaces” объясняет, как создавать и реализовывать интерфейсы.
- “Concurrency in Go” объясняет все основы параллелизма.
Во всех этих разделах есть простые практические задания, которые охватывают пройденный материал.
Шаг второй
Следующим шагом в изучении Go стал просмотр презентаций. Лучшая из них (на мой взгляд) – Go Concurrency Patterns Роба Пайка. Эту презентацию легко смотреть и она очень мотивирует. Я также рекомендую посмотреть на YouTube предложения, основанные на этом видео.
Шаг третий
Прежде чем попробовать создать свое собственное простое приложение (и начать сравнение приложений на Go с приложениями на Python), я изучил еще один замечательный учебник – Learn Go with tests. В этом учебнике рассказывается о Go и о том, как писать код, используя методологию разработки на основе тестов.
Просмотрев все эти учебники и видео, я решил наконец попробовать самостоятельно создать простое приложение на Go. Я решил написать сервис для сокращения URL-адресов, похожий на bit.ly или goo.gl, но более простой, без статистики и пользовательского интерфейса.
Сначала я попытался создать канал с набором подготовленных коротких строк для идентификации длинных URL. Это было отличное упражнение для работы с каналами и горутинами. Но пока что я не использую этот код в работе и генерирую короткие строки каждый раз, когда мне нужно сохранить длинный URL.
Далее я начал писать код для приложения (сначала – код генерации коротких строк с тестами). Когда я начал думать о хранении, я застрял. Это произошло потому, что я не понимал, как правильно разделить мой код. Для меня ответ был найден в схеме проекта стандартной библиотеки Go и в обзоре некоторых популярных библиотек и приложений на Go. Кроме того, я нашел модули Go, которые не были освещены ни в одном из учебников.
. ├── api │ └── openapi.yaml ├── build │ ├── ci │ │ └── .travis.yml │ ├── Dockerfile │ └── docker-compose.yml ├── internal │ ├── generator │ │ ├── generator.go │ │ └── generator_test.go │ └── storage │ ├── redis_storage.go │ ├── redis_storage_test.go │ ├── simple_storage.go │ ├── simple_storage_test.go │ └── storage.go ├── third_party │ ├── locustfile.py │ ├── redoc-static.html │ └── wrk_post.lua ├── .gitignore ├── .travis.yml -> build/ci/.travis.yml ├── LICENSE ├── README.md ├── go.mod ├── go.sum └── main.go
Свою вторую попытку я начал с описания схемы OpenAPI для моего API. Затем я скопировал генерирующую функцию из предыдущего кода и начал работу над хранилищем URL.
Изучая макеты проектов для приложений на Go, я нашел Bolt – простую библиотеку хранения ключевых значений, написанную на Go. В описании этой библиотеки говорится, что хранилище можно использовать для хранения большого количества данных. Я решил попробовать, поскольку используемый мною подход позволил бы легко заменить эту библиотеку на другое хранилище данных без изменения большого количества кода.
Бенчмаркинг и производительность Go vs. Python
Мое первое приложение на Go готово, и чтобы сравнить Golang с Python, я хочу провести сравнительный тест. Обычно я использую для этого wrk, но в этот раз я решил использовать Locust. С Locust проще создать тест, который будет одновременно использовать все эндпоинты, которые у меня есть.
Я был удивлен, когда результат составил около 43 запросов в секунду, что является довольно маленьким числом. Поначалу я предположил, что число так мало, потому что хранилище использует только файлы на моем диске. Я решил написать такое же приложение на Python, используя asyncio, aiohttp и aioredis в качестве хранилища, проверить результаты, а затем реализовать хранилище, использующее Redis, в приложении на Go.
И это сработало! Я получил тот же результат, что и в эталонном тесте для обоих приложений на Python и Go. Прежде всего, я запустил wrk для проверки конечной точки API для получения длинного URL по короткой строке. Это было правильное решение, потому что я получил разные результаты. Приложение Go было в 9 раз быстрее, а Python выполнял более 43 запросов за секунду. Теперь цифры составляли около 280 запросов в секунду для Python и 2500 для Go. Что, с моей точки зрения, ближе к истине.
Locust, вероятно, предлагает принцип master/slave для проведения надлежащего тестирования вместо использования fork/threads, и именно поэтому 43 запроса в секунду – это предел для Locust в текущей конфигурации.
*Распечатка результатов финальных тестов
Следующий тест был проведен для API хранения длинных URL. Он показал, что мое приложение Go может обрабатывать около 20 запросов в секунду. Это подтвердило мои ожидания. После этого теста я сделал реализацию хранения данных, использующую Redis, и снова запустил все тесты с помощью wrk.
Ниже вы можете увидеть график с результатами тестов и сравнить скорость Go и Python. Я использовал Raspberry Pi 3 B+ в качестве платформы для приложения и ноутбук с i7 и 16 Гб оперативной памяти для запуска wrk (у Locust была такая же конфигурация). Я специально использовал Raspberry Pi, чтобы дать ноутбуку, на котором я проводил тест, больше мощности.
Все эти результаты получены при работе с 12 потоками и 400 соединениями в течение 30 секунд. Цифры – это общее количество для всех потоков, а не только для одного. Если вас интересуют необработанные результаты wrk, перейдите по этой ссылке.
Мои личные результаты
Честно говоря, приложение, которое я написал на Python и запускал с помощью Gunicorn с самого начала, имело худшие результаты, чем я вам показал. Оно выдавало примерно 360 запросов в секунду. Но как только я увеличил количество воркеров Gunicorn с 2 (по умолчанию) до 4, результаты удвоились. На Raspberry Pi 4 CPU, и если воркеров больше, чем CPU, это может привести к ухудшению результатов.
Сравнивая производительность Golang и Python, я мог бы получить лучшие результаты для приложения Go, но я не знаю достаточно о том, как запускать приложение Go в продакшене.
В заключение хочу сказать, что Go мне очень понравился. В последний раз я был так заинтересован языком программирования, когда начал изучать Python. Для гибкой компании, создающей веб-приложения, Python сейчас, вероятно, лучший выбор, поскольку многие разработчики его знают. Самое впечатляющее в Python – это его огромное сообщество.
Кроме того, я считаю, что для написания приложений на Go разработчики должны больше думать о дизайне приложений, чем при использовании других языков, таких как Python. Иначе в будущем они не смогут вносить изменения и расширять функциональность приложения. Или это будет крайне сложно. Кстати, вот репозиторий с моим Go-приложением, которое я надеюсь доработать и расширить в будущем: https://github.com/Quard/gosh.
И последнее. Был ли у кого-то из вас похожий опыт изучения одного из этих языков? Каковы были ваши выводы?