Перейти к содержанию

Go vs Rust Hyper - сравнение производительности HTTP-сервера Hello world

Posted on:11 апреля 2023 г. at 08:21

Example Dynamic OG Image link В этой статье я собираюсь сравнить собственный HTTP-сервер Go с Hyper HTTP-сервером Rust. Основываясь на своих исследованиях, я обнаружил, что Hyper - самый популярный сервер на стороне Rust. Если есть лучшие и популярные альтернативы, пожалуйста, дайте мне знать.

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

Тестовая настройка

Тесты выполняются на MacBook Pro M1 с 16 ГБ оперативной памяти.

Версии программного обеспечения следующие:

Код HTTP-сервера hello world в обоих случаях выглядит следующим образом:

Go

package main

import (
  "io"
  "net/http"
)

func main() {
  http.HandleFunc("/", helloWorld)
  http.ListenAndServe(":3000", nil)
}

func helloWorld(w http.ResponseWriter, r *http.Request) {
  io.WriteString(w, "Hello world!")
}

Rust

use std::convert::Infallible;
use std::net::SocketAddr;

use bytes::Bytes;
use http_body_util::Full;
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper::{Request, Response};
use tokio::net::TcpListener;

async fn hello(_: Request<hyper::body::Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
  let response = Response::builder()
    .header("Content-type", "text/plain")
    .body(Full::new(Bytes::from("Hello World!")))
    .unwrap();
  Ok(response)
}

#[tokio::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
  let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
  let listener = TcpListener::bind(addr).await?;
  loop {
    let (stream, _) = listener.accept().await?;
    tokio::task::spawn(async move {
      if let Err(err) = http1::Builder::new()
        .serve_connection(stream, service_fn(hello)).await {
          println!("Error serving connection: {:?}", err);
        }
    });
  }
}

Cargo.toml

[package]
name = "hello_world"
version = "0.1.0"
edition = "2021"

[dependencies]
bytes = "1"
hyper = { version = "1.0.0-rc.3", features = ["full"] }
tokio = { version = "1", features = ["full"] }
http-body-util = "0.1.0-rc.2"

Код Rust был собран в режиме релиза:

> cargo build --release
...
Compiling hyper v1.0.0-rc.3
Compiling hello_world v0.1.0 (/Users/mayankc/Work/source/perfComparisons/rust)
Finished release [optimized] target(s) in 19.88s

Тестирование

Каждый тест выполняется для 5 миллионов запросов

Тесты выполняются для 10, 100 и 300 одновременных подключений.

Нагрузочный тест выполняется с помощью инструмента HTTP-тестирования Bombardier.

Ниже приведены таблицы, показывающие результаты для каждого уровня параллелизма:

По сравнению со сравнениями с Bun и Deno, на этот раз конкуренция была справедливой, потому что и Go, и Rust являются компилируемыми языками. Они создают машинный код, который выполняется напрямую. Однако с точки зрения производительности Rust превосходит его по всем аспектам.

ПОБЕДИТЕЛЬ: Rust Hyper