C# vs Rust vs Go: бенчмаркинг производительности в Kubernetes

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Простое сравнение производительности трех языков.

Введение

В этой статье обсудим создание высокопроизводительных 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-чартов и других объектов развертывания. Я внес следующие изменения:

  1. Смена Rocket на Actix в Rust

  2. Создание проекта веб-API на C# с 2 эндпоинтами и необходимыми файлами Docker

  3. Добавление файлов 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 — Под нагрузкой

p99 (99% запросов обработано):

C# — 6,10 мс, Go — 4,96 мс, Rust — 4,98 мс.

У Go здесь небольшое преимущество перед Rust, за которым следует C#.

 p90 — Под нагрузкой

p90 (90% запросов обработано):

C# — 4,55 мс, Go — 4,51 мс, Rust — 4,54 мс.

Go снова немного опережает Rust. Но C# тоже не сильно отстает.

Статистика Bombardier:

Rust
C#
Go

Самая высокая пропускная способность у C#.

Нагрузка для POST-запросов с ограничениями

Теперь давайте отправим 1 миллион запросов с 50 соединениями всем трем сервисам и ограничим скорость до 100 запросов в секунду. Однако на этот раз мы будем использовать одно соединение, которое делает 1 запрос каждую секунду.

Команда Bombardier:

bombardier -n 1000000 -m POST -c 1 -r 1 <URL>
Нагрузка на ЦП — Нагрузка POST-запросами

Наивысшая нагрузка на процессор здесь у Golang.

Нагрузка на память — Нагрузка POST-запросами

В данном случае сильнее всех нагружает память C#.

 p99 — Нагрузка POST-запросами

Замеры латентности демонстрируют интересные данные. Латентность у Go пытается идти в ногу с C#, но возникают сложности с консистентностью. Rust отлично показывает себя с латентностью вдвое ниже, чем у C#.

p90 — Нагрузка POST-запросами

Опять же, у Rust удивительно низкая латентность.

Статистика Bombardier:

Rust
C#
 Go

Заключение

Приведенные в этой статье бенчмарки дают базовое понимание производительности трех языков, но не учитывают сложных сценариев. Если вас интересуют бенчмарки веб-фреймворков, я бы порекомендовал зайти на TechEmpower Framework Benchmarks.

На основе полученных данных можно заключить что Rust демонстрирует стабильную производительность и почти всегда быстрее, чем C# и Go. Однако это ожидаемо, так как Rust работает непосредственно с железом.

В случае с C# и Go производительность неоднородна, поскольку эти два ЯП превосходят друг друга в различных сценариях.

Каждый язык хорош для конкретных сценариев и определенных целей ⬇️

Если вам необходимы знания в Go, предлагаем посмотреть в сторону курса Golang для инженеров. Вы научитесь создавать свой API-сервер, запускать контейнеры, взаимодействовать с Docker и работать с кастомными операторами.

Старт потока — 15 мая. Стать участником: https://slurm.club/3GVCBv8

Источник: https://habr.com/ru/companies/southbridge/articles/730102/


Интересные статьи

Интересные статьи

В кластере Kubernetes нам доступен любой сервис в любом пространстве имён, то есть по умолчанию pod открыт для любого трафика.Мы можем определить сетевую политику для пространства имён или pod’а, чтоб...
В современных реалиях информационная безопасность стала важнейшим аспектом в жизни IT-систем. Количество атак постоянно растет, появляются новые виды угроз. Постоянно происходят утечки информации. Одн...
Представляю вашему вниманию ваш будущий маленький Teamviewer. Полностью открытый, с клиентами на все платформы. Заявлено небольшое потребление серверных ресурсов. Из коробки умеет ходить через наты, к...
Привет! Мы – часть команды разработки «Рамблер/Медиа» (портал «Рамблер»). На протяжении трех лет мы поддерживаем и развиваем несколько больших python-приложений. Чуть больше года назад ...
В этом бардаке, который сейчас происходит в мире, легко забыть, что прошло уже пять лет с выпуска 1.0 в 2015 году! Rust за эти пять лет сильно изменился, так что мы хотели бы вспомнит...