Skip to content

Instantly share code, notes, and snippets.

@r0mdau
Last active December 28, 2023 23:33
Show Gist options
  • Save r0mdau/ac0f416d2305e33a14d3c754b7bde27a to your computer and use it in GitHub Desktop.
Save r0mdau/ac0f416d2305e33a14d3c754b7bde27a to your computer and use it in GitHub Desktop.
Rust Actix vs Rust Hyper vs Go fasthttp vs Go net/http httprouter

Load tests

Injector

wrk is the binary used as injector, always used with these options:

./wrk -t12 -c1000 -d15s http://127.0.0.1:8080/

Results

Processor : Intel(R) Xeon(R) CPU E5-1650 0 @ 3.20GHz

$ uname -a
Linux home 4.19.0-17-amd64 #1 SMP Debian 4.19.194-3 (2021-07-18) x86_64 GNU/Linux
$ go version
go version go1.16.5 linux/amd64
$ rustc --version
rustc 1.54.0 (a178d0322 2021-07-26)

Summary

Language Framework Requests/sec Transfer/sec Thread latency (avg) Thread latency (max) Compilation time
Rust Actix 294315.51 34.80MB 4.61ms 56.76ms 72.428s
Rust Hyper 299736.48 38.02MB 3.45ms 206.41ms 50.062s
Go httprouter 187968.82 22.23MB 7.08ms 102.06ms 0.214s
Go fasthttp 334184.91 42.39MB 4.20ms 115.86ms 0.491s

Rust Actix

Running 15s test @ http://127.0.0.1:8080
  12 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.61ms    5.43ms  56.76ms   84.52%
    Req/Sec    24.77k     3.66k   58.62k    72.03%
  4442997 requests in 15.10s, 525.41MB read
Requests/sec: 294315.51
Transfer/sec:     34.80MB

Rust Hyper

Running 15s test @ http://127.0.0.1:8080
  12 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.45ms    3.11ms 206.41ms   84.82%
    Req/Sec    25.21k     3.33k   50.15k    75.17%
  4518451 requests in 15.07s, 573.11MB read
Requests/sec: 299736.48
Transfer/sec:     38.02MB

Go httprouter

Running 15s test @ http://127.0.0.1:8080
  12 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     7.08ms    8.33ms 102.06ms   87.05%
    Req/Sec    15.83k     3.07k   36.62k    71.27%
  2836680 requests in 15.09s, 335.45MB read
Requests/sec: 187968.82
Transfer/sec:     22.23MB

Go fasthttp

Running 15s test @ http://127.0.0.1:8080
  12 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.20ms    6.00ms 115.86ms   88.23%
    Req/Sec    28.18k     5.64k   63.95k    70.16%
  5041567 requests in 15.09s, 639.47MB read
Requests/sec: 334184.91
Transfer/sec:     42.39MB

Configurations

You will find source code as separate files in this gist.

Rust Actix

Build

cargo build --release

Cargo.toml

[package]
name = "actix"
version = "0.1.0"
edition = "2018"

[dependencies]
actix-web = "3.3.2"

Run

./target/release/actix

Rust Hyper

Build

cargo build --release

Cargo.toml

[package]
name = "hyper"
version = "0.1.0"
edition = "2018"

[profile.release]
lto = true

[dependencies]
hyper = { version = "0.14.11", features = ["full"] }
tokio = { version = "1.10.0", features = ["full"] }

Run

./target/release/hyper

Go httprouter

Build

go mod init example.com/httprouter
go get github.com/julienschmidt/httprouter@v1.3.0
go build -ldflags "-s -w" httprouter.go

Run

./httprouter

Go fasthttp

Build

go mod init example.com/fashttp
go get github.com/valyala/fasthttp@v1.28.0
go build -ldflags "-s -w" fasthttp.go

Run

./fasthttp
use actix_web::{web, Responder, middleware, App, HttpServer};
async fn health_check() -> impl Responder {
"Welcome!"
}
fn routes(cfg: &mut web::ServiceConfig) {
cfg.route("/", web::get().to(health_check));
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let serv = HttpServer::new(move || {
App::new()
.wrap(middleware::Compress::default())
.configure(routes)
});
serv.bind("127.0.0.1:8080")?
.run()
.await
}
package main
import (
"github.com/valyala/fasthttp"
)
func requestHandler(ctx *fasthttp.RequestCtx) {
switch string(ctx.Path()) {
case "/":
ctx.Success("application/json", []byte("Welcome!"))
default:
ctx.Error("Unsupported path", fasthttp.StatusNotFound)
}
}
func main() {
// pass plain function to fasthttp
fasthttp.ListenAndServe(":8080", requestHandler)
}
package main
import (
"fmt"
"github.com/julienschmidt/httprouter"
"net/http"
)
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "Welcome!")
}
func main() {
router := httprouter.New()
router.GET("/", Index)
http.ListenAndServe(":8080", router)
}
use std::{convert::Infallible, net::SocketAddr};
use hyper::{Body, Request, Response, Server};
use hyper::service::{make_service_fn, service_fn};
use hyper::header::{CONTENT_TYPE, CONTENT_LENGTH, SERVER};
const RESP: &str = "Welcome!";
async fn handle(_: Request<Body>) -> Result<Response<Body>, Infallible> {
let mut resp = Response::new(RESP.into());
resp
.headers_mut()
.insert(SERVER, "rusthttp".parse().unwrap());
resp
.headers_mut()
.insert(CONTENT_TYPE, "application/json".parse().unwrap());
resp
.headers_mut()
.insert(CONTENT_LENGTH, RESP.len().to_string().parse().unwrap());
Ok(resp)
}
#[tokio::main]
async fn main() {
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
let make_svc = make_service_fn(|_conn| async {
Ok::<_, Infallible>(service_fn(handle))
});
let server = Server::bind(&addr)
.serve(make_svc);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment