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 -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)
}
}