Простое сравнение производительности трех языков.
Введение
В этой статье обсудим создание высокопроизводительных web-API на Rust, C# и Go и их развертывание в кластере Kubernetes. Также узнаем, как отслеживать использование ресурсов этими API с помощью инструментов мониторинга производительности.
Эта статья расширяет работу, проведенную Anton Putra. Ссылка на его видео. Мы создадим по одному приложению на каждом языке и посмотрим, насколько хорошо они работают.
Для понимания статьи вам понадобятся базовые знания Rust, C#, Go, Docker, Terraform и Kubernetes.
Создание web-API
Мы создадим два веб-API: одно на Rust, другое на C#. Каждый API будет иметь два метода. Первый метод будет использоваться для хранения информации, а второй — для ее получения.
На Rust мы можем использовать Actix. Этот фреймворк предоставляет простой и эффективный способ создания веб-API. Создать методы хранения и извлечения можно с помощью библиотеку Serde для сериализации и десериализации данных.
На C# можно создать веб-API, используя ASP.NET Core. Тоже простой и эффективный фреймворк. Для создания методов хранения и извлечения мы можем использовать Entity Framework Core для взаимодействия с базой данных.
Я клонировал этот репозиторий и отредактировал код, чтобы он соответствовал целям статьи. Ссылку на отредактированный репозиторий можно найти здесь: shyamsundarb-arch/rustvcsvgo (github.com). Посмотрите видео Anton Putra, чтобы понять Helm-чартов и других объектов развертывания. Я внес следующие изменения:
Смена Rocket на Actix в Rust
Создание проекта веб-API на C# с 2 эндпоинтами и необходимыми файлами Docker
Добавление файлов Kubernetes для проекта на C#.
Оценка использования ресурсов с помощью инструментов мониторинга производительности
Чтобы убедиться, что наши API работают эффективно, мы будем использовать инструменты мониторинга производительности для отслеживания использования ресурсов. Эти инструменты предоставят нам информацию о нагрузке на процессор и память API вместе с любыми другими метриками производительности, которые нужно отследить.
Эта информация поможет нам выявить те области API, которые нуждаются в оптимизации и внесении изменений для улучшения производительности. Визуализировать данные будем в Grafana.
Первоначальные наблюдения
Базовые бенчмарки запуска приложения без какой-либо нагрузки приведены ниже.
Красная линия демонстрирует Rust, синяя представляет Go, а зеленая — C#.
В состоянии покоя нагрузка на процессор у Rust превышает показатели C#, которые в свою очередь немного выше, чем у Go. Нагрузка на память в состоянии покоя больше всего у C#, далее идет Go, затем Rust. Нагрузка на память у Rust минимальна.
Нагрузка для GET-запросов
Мы применим отличный инструмент, который называется Bombardier.
Сначала мы отправим 1 миллион запросов с 50 соединениями всем трем сервисам и ограничим скорость до 100 RPS.
Команда выглядит так:
bombardier -n 1000000 -m GET -c 50 -r 100 <URL>
C# и Go нагружают процессор практически одинаково, тогда как нагрузка у Rust гораздо меньше, чем в двух предыдущих ЯП.
В данном случае нагрузка на память у C# заметно выше, далее следует Go. Нагрузка на память у Rust вообще не изменилась. Это впечатляет.
Давайте посмотрим на латентности.
p99 (99% запросов обработано):
C# — 6,10 мс, Go — 4,96 мс, Rust — 4,98 мс.
У Go здесь небольшое преимущество перед Rust, за которым следует C#.
p90 (90% запросов обработано):
C# — 4,55 мс, Go — 4,51 мс, Rust — 4,54 мс.
Go снова немного опережает Rust. Но C# тоже не сильно отстает.
Статистика Bombardier:
Самая высокая пропускная способность у C#.
Нагрузка для POST-запросов с ограничениями
Теперь давайте отправим 1 миллион запросов с 50 соединениями всем трем сервисам и ограничим скорость до 100 запросов в секунду. Однако на этот раз мы будем использовать одно соединение, которое делает 1 запрос каждую секунду.
Команда Bombardier:
bombardier -n 1000000 -m POST -c 1 -r 1 <URL>
Наивысшая нагрузка на процессор здесь у Golang.
В данном случае сильнее всех нагружает память C#.
Замеры латентности демонстрируют интересные данные. Латентность у Go пытается идти в ногу с C#, но возникают сложности с консистентностью. Rust отлично показывает себя с латентностью вдвое ниже, чем у C#.
Опять же, у Rust удивительно низкая латентность.
Статистика Bombardier:
Заключение
Приведенные в этой статье бенчмарки дают базовое понимание производительности трех языков, но не учитывают сложных сценариев. Если вас интересуют бенчмарки веб-фреймворков, я бы порекомендовал зайти на TechEmpower Framework Benchmarks.
На основе полученных данных можно заключить что Rust демонстрирует стабильную производительность и почти всегда быстрее, чем C# и Go. Однако это ожидаемо, так как Rust работает непосредственно с железом.
В случае с C# и Go производительность неоднородна, поскольку эти два ЯП превосходят друг друга в различных сценариях.
Каждый язык хорош для конкретных сценариев и определенных целей ⬇️
Если вам необходимы знания в Go, предлагаем посмотреть в сторону курса Golang для инженеров. Вы научитесь создавать свой API-сервер, запускать контейнеры, взаимодействовать с Docker и работать с кастомными операторами.
Старт потока — 15 мая. Стать участником: https://slurm.club/3GVCBv8
Комментарии (12)
sergey_prokofiev
19.04.2023 11:58+14Мда уж.
В состоянии покоя нагрузка на процессор у Rust превышает показатели C#, которые в свою очередь немного выше, чем у Go.
Вы это вообще серьезно? Вы понимаете как кампутир работает, или абы какие то цифры воткнуть?В данном случае сильнее всех нагружает память C#.
Очень похоже что не понимаете.
Латентность у Go пытается идти в ногу с C#, но возникают сложности с консистентностью. Rust отлично показывает себя с латентностью вдвое ниже, чем у C#.
Я тоже так умею, и даже возможно чуть лучше:
С точки зрения банальной эрудиции не каждый здравомыслящий индивидуум способен игнорировать тенденции парадоксальных эмоций, поэтом считаем обсуждаемый воброс нереентабельным и не подлежащим обсуждению.
xijinpooh
19.04.2023 11:58+1А можно для непосвящённых, что не так с цифрами?
mayorovp
19.04.2023 11:58+1Не то с цифрами то, что в состоянии покоя нагрузка на процессор нулевая, на то оно и состояние покоя. Ну, там в фоне может ещё работать сборщик мусора, однако и он не может работать долго и давать стабильную нагрузку.
И вот, сравнивая три нуля, автор рисует какие-то различающиеся графики и делает по ним выводы.
sergey_prokofiev
19.04.2023 11:58+2с цифрами может все и так. Но вот с выбором цифр и их трактовкой все очень, очень плохо.
В состоянии покоя нагрузка на процессор у Rust превышает показатели C#
Как может в принципе язык(sic!) программирования(sic!) «в состоянии покоя» что-то нагружать?
Я догадываюсь, что подразумевается запущенный веб сервер(непонятно какой) с (непонятно какой) конфигурацией, который в состоянии прослушивания портов создает некую нагрузку на железо, обусловленное, в том числе и рантаймом указанных языков программирования, но тут же миллион вопросов - что конкретно и в каких условиях меряли, и главное, зачем? И причем тут языки программирования, вообще непонятно .
И дальше все только хуже, лень расписывать :))
Ktator
19.04.2023 11:58+2Раз уж мы говорим про high performance, то почему, например, в Go не используется fasthttp? https://github.com/valyala/fasthttp
Mes
19.04.2023 11:58Курсовую работу пишете?)
hello_my_name_is_dany
19.04.2023 11:58+3Это они сделали транскрипт чужого видео и в конце вставили свою рекламу
MrAlone
19.04.2023 11:58+1На основе полученных данных можно заключить что Rust демонстрирует стабильную производительность и почти всегда быстрее, чем C#
Я чего-то не понимаю. Производительность в данном случае - пропускная способность, которая в одном случае у C# выше на 10%, а во втором - на весьма весомые 30%.
DBalashov
19.04.2023 11:58+6Совершенно бессмысленная статья с рекламой курсов в последнем абзаце.
Я бы даже не стал читать после первой фразы "Простое сравнение производительности трех языков", но любопытство пересилило.
Что тут измеряется? Перф http-стека? Где версии фреймворков? Где конфигурации подов (ядра, память)? Также не вижу какие данные и сколько в POST отправляются. В итоге возвращаемся к исходному вопросу - ЧТО тут меряется?
ivankudryavtsev
Мне кажется, что k8s притянут за уши баззвордов ради...