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

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

a2a702c61f7a55fed5eba0908cb3aa8d.png

Введение

973c83208f8ef0442061699a86758e63.webp

В этой статье обсудим создание высокопроизводительных 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 

24eaf86980d4f3f7b14d7a4da9e55923.webp

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

C# и Go нагружают процессор практически одинаково, тогда как нагрузка у Rust гораздо меньше, чем в двух предыдущих ЯП.

Нагрузка на память — Под нагрузкой

Нагрузка на память — Под нагрузкой

В данном случае нагрузка на память у C# заметно выше, далее следует Go. Нагрузка на память у Rust вообще не изменилась. Это впечатляет.

Давайте посмотрим на латентности.

7cb8e4b4a728b528450c7ea91d4d5438.webp

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

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

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

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

3d1551f88c6be0e1854f9b25cad52eec.webp

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

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

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

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

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

bbe95c8de584e04e1542b26d3613a2c4.webp

Rust

353222bb25d7dcd0a84ea87e12fe73ab.webp

C#

b29bf0236c51376d7ddc4f7f69c790ce.webp

Go

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

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

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

Команда Bombardier:

bombardier -n 1000000 -m POST -c 1 -r 1 

afe618170c103b987f1b014e24f54664.webp

Нагрузка на ЦП — Нагрузка POST-запросами

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

4ac6c29643695177a5c9577c05d50821.webp

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

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

431c9490f5dc18fbac3b5cc6bd0c5866.webp

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

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

cf66fd89b15c6d846db1f398a16b37e8.webp

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

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

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

bdbd0b56695d43cceb260479dd59667d.webp

Rust

d20155e134892616907b20bbdc8d302d.webp

C#

981e238b6485849badcbd82553a2c794.webp

 Go

Заключение

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

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

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

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

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

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

© Habrahabr.ru