Skip to content

Benchmark

Results

These performance tests are not final; results vary from different test environments.

Server configuration

  • OS:Ubuntu23.04
  • Number of cores: 4
  • Memory: 4G
  • CPU: AMD Ryzen 5 3600 6-Core Processor

Test tool

wrk

./wrk -t4 -c1000 -d90s --latency

Result

ZEDIO:

ZEDIO
cpp
#include "zedio/core.hpp"
#include "zedio/log.hpp"
#include "zedio/net.hpp"
#include "zedio/signal.hpp"
#include "zedio/time.hpp"

#include <string_view>

using namespace zedio::async;
using namespace zedio::net;
using namespace zedio::log;
using namespace zedio;

constexpr std::string_view response = R"(
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 13

Hello, World!
)";

auto process(TcpStream stream) -> Task<void> {
    char buf[128];
    while (true) {
        if (auto ret = co_await stream.read(buf); !ret || ret.value() == 0) {
            break;
        }
        if (auto ret = co_await stream.write_all(response); !ret) {
            break;
        }
    }
}

auto server() -> Task<void> {
    auto has_addr = SocketAddr::parse("192.168.15.33", 8888);
    if (!has_addr) {
        console.error(has_addr.error().message());
        co_return;
    }
    auto has_listener = TcpListener::bind(has_addr.value());
    if (!has_listener) {
        console.error(has_listener.error().message());
        co_return;
    }
    auto listener = std::move(has_listener.value());
    while (true) {
        auto has_stream = co_await listener.accept();
        if (has_stream) {
            auto &[stream, peer_addr] = has_stream.value();
            LOG_INFO("Accept a connection from {}", peer_addr);
            spawn(process(std::move(stream)));
        } else {
            LOG_ERROR("{}", has_stream.error());
            break;
        }
    }
}

auto main_loop() -> Task<void> {
    spawn(server());
    co_await signal::ctrl_c();
}

auto main(int argc, char **argv) -> int {
    if (argc != 2) {
        std::cerr << "usage: benchmark num_threas\n";
        return -1;
    }
    SET_LOG_LEVEL(zedio::log::LogLevel::Trace);
    auto num_threads = std::stoi(argv[1]);
    Runtime::options()
        .scheduler()
        .set_num_workers(num_threads)
        .driver()
        .set_submit_interval(0)
        .build()
        .block_on(main_loop());
    return 0;
}

TOKIO:

TOKIO
rs
use tokio::io::AsyncReadExt;
use tokio::io::AsyncWriteExt;
use tokio::net::{TcpListener, TcpStream};

const RESPONSE: &str = r"
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 13

Hello, World!";

// #[tokio::main(worker_threads = 4)]
#[tokio::main]
async fn main() {
    let listener = TcpListener::bind("192.168.15.33:7777").await.unwrap();
    loop {
        let (stream, _) = listener.accept().await.unwrap();
        tokio::spawn(async move {
            process(stream).await;
        });
    }
}

async fn process(mut stream: TcpStream) {
    let mut buf = [0; 128];
    loop {
        match stream.read(&mut buf).await {
            Ok(len) => {
                if len == 0 {
                    return;
                } else {
                    stream.write_all(RESPONSE.as_bytes()).await.unwrap()
                }
            }
            Err(err) => println!("error: {}", err),
        }
    }
}

MONOIO:

MONOIO
rs
use monoio::io::{AsyncReadRent, AsyncWriteRentExt};
use monoio::net::{TcpListener, TcpStream};

const RESPONSE: &str = r"
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 13

Hello, World!";


#[monoio::main(worker_threads = 4)]
// #[monoio::main]
async fn main() {
    let listener = TcpListener::bind("192.168.15.33:6666").unwrap();
    println!("listening");
    loop {
        let (stream, _) = listener.accept().await.unwrap();
        monoio::spawn(process(stream));
    }
}


async fn process(mut stream: TcpStream) -> std::io::Result<()> {
    let mut buf: Vec<u8> = Vec::with_capacity(128);
    let mut res;
    loop {
        // read
        (res, buf) = stream.read(buf).await;
        if res? == 0 {
            return Ok(());
        }

        (_, _) = stream.write_all(RESPONSE).await;
    }
}

GOLANG:

GOLANG
go
package main

import (
	"fmt"
	"log"
	"net"
)

const response = `HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 13

Hello, World!
`

func worker(conn net.Conn) {
	defer conn.Close()
	b := make([]byte, 128)
	for {
		_, err := conn.Read(b)
		if err != nil {
			break
		}
		_, err = conn.Write([]byte(response))
		if err != nil {
			break
		}
	}
}

func main() {
	listner, err := net.Listen("tcp", "192.168.15.33:7878")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Listering on %v\n", listner.Addr())
	for {
		conn, err := listner.Accept()
		if err != nil {
			break
		}
		go worker(conn)
	}
}