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

Введение

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

Комментарии (12)


  1. ivankudryavtsev
    19.04.2023 11:58
    +16

    Мне кажется, что k8s притянут за уши баззвордов ради...


  1. sergey_prokofiev
    19.04.2023 11:58
    +14

    Мда уж.

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

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

    Очень похоже что не понимаете.

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

    Я тоже так умею, и даже возможно чуть лучше:

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


    1. unclejocker
      19.04.2023 11:58

      Нет, так вы слона (курсы) не продадите.


    1. xijinpooh
      19.04.2023 11:58
      +1

      А можно для непосвящённых, что не так с цифрами?


      1. mayorovp
        19.04.2023 11:58
        +1

        Не то с цифрами то, что в состоянии покоя нагрузка на процессор нулевая, на то оно и состояние покоя. Ну, там в фоне может ещё работать сборщик мусора, однако и он не может работать долго и давать стабильную нагрузку.


        И вот, сравнивая три нуля, автор рисует какие-то различающиеся графики и делает по ним выводы.


      1. sergey_prokofiev
        19.04.2023 11:58
        +2

        с цифрами может все и так. Но вот с выбором цифр и их трактовкой все очень, очень плохо.

        В состоянии покоя нагрузка на процессор у Rust превышает показатели C#

        Как может в принципе язык(sic!) программирования(sic!) «в состоянии покоя» что-то нагружать?

        Я догадываюсь, что подразумевается запущенный веб сервер(непонятно какой) с (непонятно какой) конфигурацией, который в состоянии прослушивания портов создает некую нагрузку на железо, обусловленное, в том числе и рантаймом указанных языков программирования, но тут же миллион вопросов - что конкретно и в каких условиях меряли, и главное, зачем? И причем тут языки программирования, вообще непонятно .

        И дальше все только хуже, лень расписывать :))


  1. Ktator
    19.04.2023 11:58
    +2

    Раз уж мы говорим про high performance, то почему, например, в Go не используется fasthttp? https://github.com/valyala/fasthttp


    1. t38c3j
      19.04.2023 11:58
      +3

      A в Rust hyper https://hyper.rs/


  1. Mes
    19.04.2023 11:58

    Курсовую работу пишете?)


    1. hello_my_name_is_dany
      19.04.2023 11:58
      +3

      Это они сделали транскрипт чужого видео и в конце вставили свою рекламу


  1. MrAlone
    19.04.2023 11:58
    +1

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

    Я чего-то не понимаю. Производительность в данном случае - пропускная способность, которая в одном случае у C# выше на 10%, а во втором - на весьма весомые 30%.


  1. DBalashov
    19.04.2023 11:58
    +6

    Совершенно бессмысленная статья с рекламой курсов в последнем абзаце.

    Я бы даже не стал читать после первой фразы "Простое сравнение производительности трех языков", но любопытство пересилило.

    Что тут измеряется? Перф http-стека? Где версии фреймворков? Где конфигурации подов (ядра, память)? Также не вижу какие данные и сколько в POST отправляются. В итоге возвращаемся к исходному вопросу - ЧТО тут меряется?