Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
330 changes: 310 additions & 20 deletions frameworks/Rust/xitca-web/Cargo.lock

Large diffs are not rendered by default.

39 changes: 26 additions & 13 deletions frameworks/Rust/xitca-web/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ edition = "2024"
[[bin]]
name = "xitca-web"
path = "./src/main.rs"
required-features = ["io-uring", "pg", "router", "template"]
required-features = ["io-uring", "pg", "router", "template", "zero-copy"]

[[bin]]
name = "xitca-web-compio"
path = "./src/main_compio.rs"
required-features = ["compio", "perf", "perf-json", "pg", "router", "template", "zero-copy"]

[[bin]]
name = "xitca-web-barebone"
path = "./src/main_barebone.rs"
required-features = ["perf", "perf-json", "pg", "template"]
required-features = ["perf", "perf-json", "pg", "template", "zero-copy"]

[[bin]]
name = "xitca-web-diesel"
Expand All @@ -38,13 +43,17 @@ web-codegen = ["xitca-web/codegen", "xitca-web/urlencoded"]
template = ["dep:sailfish"]
# io-uring optional
io-uring = ["dep:tokio-uring", "xitca-http/io-uring", "xitca-server/io-uring"]
# zero-copy database row parsing optional(not supported by ORMs)
zero-copy = []
# unrealistic performance optimization
perf = ["perf-allocator", "dep:core_affinity", "tokio/parking_lot"]
perf-allocator = ["dep:mimalloc"]
# regular json serializer
json = ["serde_json"]
# performance optimization json serializer
perf-json = ["sonic-rs"]
# compio as runtime
compio = ["dep:compio", "dep:socket2", "xitca-http/compio", "xitca-postgres/compio"]

[dependencies]
xitca-http = "0.7"
Expand All @@ -70,7 +79,7 @@ xitca-postgres-diesel = { version = "0.2", default-features = false, optional =
futures-util = { version = "0.3", default-features = false, optional = true }

# toasty orm optional
xitca-postgres-toasty = { version = "0.1", optional = true }
xitca-postgres-toasty = { version = "0.1", features = ["nightly"], optional = true }
toasty = { version = "0.1", optional = true }

# template optional
Expand All @@ -87,6 +96,10 @@ mimalloc = { version = "0.1", default-features = false, optional = true }
serde_json = { version = "1", optional = true }
sonic-rs = { version = "0.5.6", optional = true }

# compio optional
compio = { version = "0.17", features = ["time"], optional = true }
socket2 = { version = "0.6", optional = true }

futures-core = { version = "0.3", default-features = false }
rand = { version = "0.9", features = ["os_rng", "small_rng"], default-features = false }
tokio = "1.48"
Expand All @@ -99,18 +112,18 @@ panic = "abort"

[patch.crates-io]
xitca-postgres-diesel = { git = "https://github.com/fakeshadow/xitca-postgres-diesel", rev = "b6d1922" }
xitca-postgres-toasty = { git = "https://github.com/fakeshadow/xitca-postgres-toasty", rev = "6034a22" }
xitca-postgres-toasty = { git = "https://github.com/fakeshadow/xitca-postgres-toasty", rev = "6e91d2f" }

toasty = { git = "https://github.com/fakeshadow/toasty", branch = "engine" }
toasty-core = { git = "https://github.com/fakeshadow/toasty", branch = "engine" }
toasty-sql = { git = "https://github.com/fakeshadow/toasty", branch = "engine" }
toasty = { git = "https://github.com/fakeshadow/toasty", rev = "51c8186" }
toasty-core = { git = "https://github.com/fakeshadow/toasty", rev = "51c8186" }
toasty-sql = { git = "https://github.com/fakeshadow/toasty", rev = "51c8186" }

# personal fork of tokio-uring with tokio local runtime enabled
tokio-uring = { git = "http://github.com/fakeshadow/tokio-uring", rev = "c3d5887" }

xitca-codegen = { git = "http://github.com/HFQR/xitca-web", rev = "3f38156" }
xitca-http = { git = "http://github.com/HFQR/xitca-web", rev = "3f38156" }
xitca-postgres = { git = "http://github.com/HFQR/xitca-web", rev = "3f38156" }
xitca-server = { git = "http://github.com/HFQR/xitca-web", rev = "3f38156" }
xitca-service = { git = "http://github.com/HFQR/xitca-web", rev = "3f38156" }
xitca-web = { git = "http://github.com/HFQR/xitca-web", rev = "3f38156" }
xitca-codegen = { git = "http://github.com/HFQR/xitca-web", rev = "b3d7dc0be3783fe1ad66ba94236c3a1dcfc1af8b" }
xitca-http = { git = "http://github.com/HFQR/xitca-web", rev = "b3d7dc0be3783fe1ad66ba94236c3a1dcfc1af8b" }
xitca-postgres = { git = "http://github.com/HFQR/xitca-web", rev = "b3d7dc0be3783fe1ad66ba94236c3a1dcfc1af8b" }
xitca-server = { git = "http://github.com/HFQR/xitca-web", rev = "b3d7dc0be3783fe1ad66ba94236c3a1dcfc1af8b" }
xitca-service = { git = "http://github.com/HFQR/xitca-web", rev = "b3d7dc0be3783fe1ad66ba94236c3a1dcfc1af8b" }
xitca-web = { git = "http://github.com/HFQR/xitca-web", rev = "b3d7dc0be3783fe1ad66ba94236c3a1dcfc1af8b" }
22 changes: 22 additions & 0 deletions frameworks/Rust/xitca-web/benchmark_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,28 @@
"notes": "",
"versus": ""
},
"compio": {
"json_url": "/json",
"plaintext_url": "/plaintext",
"db_url": "/db",
"fortune_url": "/fortunes",
"query_url": "/queries?q=",
"update_url": "/updates?q=",
"port": 8080,
"approach": "Realistic",
"classification": "Platform",
"database": "Postgres",
"framework": "xitca-web",
"language": "Rust",
"orm": "Raw",
"platform": "None",
"webserver": "xitca-server",
"os": "Linux",
"database_os": "Linux",
"display_name": "xitca-web [compio]",
"notes": "",
"versus": ""
},
"diesel": {
"db_url": "/db",
"fortune_url": "/fortunes",
Expand Down
79 changes: 79 additions & 0 deletions frameworks/Rust/xitca-web/src/db_compio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//! this module is unrealistic. related issue:
//! https://github.com/TechEmpower/FrameworkBenchmarks/issues/8790

use core::{
async_iter::AsyncIterator,
future::{Future, poll_fn},
pin::pin,
};

use xitca_postgres::{Execute, statement::Statement};

use super::{
db::Exec,
ser::{Fortunes, World},
util::{DB_URL, HandleResult},
};

pub struct Client {
cli: xitca_postgres::Client,
exec: Exec,
fortune: Statement,
world: Statement,
update: Statement,
}

impl Client {
pub async fn create() -> HandleResult<Self> {
let (cli, drv) = compio::runtime::spawn_blocking(|| {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(xitca_postgres::Postgres::new(DB_URL).connect())
})
.await
.unwrap()?;

let drv = drv.try_into_tcp().expect("raw tcp is used for database connection");
let drv = xitca_postgres::CompIoDriver::from_tcp(drv)?;

compio::runtime::spawn(async move {
let mut drv = pin!(drv.into_async_iter());
while poll_fn(|cx| drv.as_mut().poll_next(cx)).await.is_some() {}
})
.detach();

let world = Exec::WORLD_STMT.execute(&cli).await?.leak();
let fortune = Exec::FORTUNE_STMT.execute(&cli).await?.leak();
let update = Exec::UPDATE_STMT.execute(&cli).await?.leak();

Ok(Self {
cli,
exec: Default::default(),
world,
fortune,
update,
})
}

#[inline]
pub fn db(&self) -> impl Future<Output = HandleResult<World>> {
self.exec.db(&self.cli, &self.world)
}

#[inline]
pub fn queries(&self, num: u16) -> impl Future<Output = HandleResult<Vec<World>>> {
self.exec.queries(&self.cli, &self.world, num)
}

#[inline]
pub fn updates(&self, num: u16) -> impl Future<Output = HandleResult<Vec<World>>> {
self.exec.updates(&self.cli, &self.world, &self.update, num)
}

#[inline]
pub fn fortunes(&self) -> impl Future<Output = HandleResult<Fortunes>> {
Exec::fortunes(&self.cli, &self.fortune)
}
}
30 changes: 11 additions & 19 deletions frameworks/Rust/xitca-web/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// temporary allocator for tracking overhead between xitca-web and xitca-web [barebone] bench.
// remove it before official run
#[cfg(feature = "perf-allocator")]
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;

Expand All @@ -14,8 +13,8 @@ use xitca_http::{
bytes::Bytes,
h1::RequestBody,
http::{
self, IntoResponse as _, RequestExt, StatusCode,
const_header_value::{TEXT_HTML_UTF8, TEXT_UTF8},
self, HeaderValue, IntoResponse as _, RequestExt, StatusCode,
const_header_value::{JSON, TEXT_HTML_UTF8, TEXT_UTF8},
header::{CONTENT_TYPE, SERVER},
},
util::{
Expand All @@ -28,8 +27,8 @@ use xitca_http::{
};
use xitca_service::{Service, ServiceExt, fn_service};

use ser::{HELLO_BYTES, Message};
use util::{HandleResult, QueryParse, SERVER_HEADER_VALUE};
use ser::{HELLO, Message};
use util::{HandleResult, QueryParse};

type Request<B> = http::Request<RequestExt<B>>;

Expand All @@ -43,7 +42,7 @@ fn main() -> std::io::Result<()> {
"/plaintext",
get(fn_service(async |ctx: Ctx| {
let (req, _) = ctx.into_parts();
let mut res = req.into_response(const { Bytes::from_static(HELLO_BYTES) });
let mut res = req.into_response(const { Bytes::from_static(HELLO.as_bytes()) });
res.headers_mut().insert(CONTENT_TYPE, TEXT_UTF8);
Ok(res)
})),
Expand All @@ -52,7 +51,7 @@ fn main() -> std::io::Result<()> {
"/json",
get(fn_service(async |ctx: Ctx| {
let (req, _) = ctx.into_parts();
json_response(req, &Message::new())
json_response(req, Message::HELLO)
})),
)
.insert(
Expand Down Expand Up @@ -91,7 +90,7 @@ fn main() -> std::io::Result<()> {
.enclosed(ContextBuilder::new(db_pool::Client::create))
.enclosed_fn(async |service, req| {
let mut res = service.call(req).await.unwrap_or_else(error_handler);
res.headers_mut().insert(SERVER, SERVER_HEADER_VALUE);
res.headers_mut().insert(SERVER, HeaderValue::from_static("x"));
Ok::<_, core::convert::Infallible>(res)
})
.enclosed(HttpServiceBuilder::h1().io_uring());
Expand All @@ -118,16 +117,9 @@ fn error_handler(e: RouterError<util::Error>) -> Response {
.unwrap()
}

#[cfg(any(feature = "json", feature = "perf-json"))]
fn json_response<Ext>(req: Request<Ext>, val: &impl serde_core::Serialize) -> HandleResult<Response> {
let mut buf = xitca_http::bytes::BytesMut::new();
#[cfg(all(feature = "json", not(feature = "perf-json")))]
serde_json::to_writer(xitca_http::bytes::BufMutWriter(&mut buf), val)?;

#[cfg(all(feature = "perf-json", not(feature = "json")))]
sonic_rs::to_writer(xitca_http::bytes::BufMut::writer(&mut buf), val)?;

let mut res = req.into_response(buf.freeze());
res.headers_mut().insert(CONTENT_TYPE, http::const_header_value::JSON);
let buf = ser::json_serialize(val)?;
let mut res = req.into_response(Bytes::from(buf));
res.headers_mut().insert(CONTENT_TYPE, JSON);
Ok(res)
}
32 changes: 10 additions & 22 deletions frameworks/Rust/xitca-web/src/main_barebone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,13 @@ mod util;
use std::io;

use xitca_http::{
bytes::BufMut,
h1::dispatcher_unreal::{Dispatcher, Request, Response},
http::StatusCode,
};
use xitca_service::Service;

use self::{
ser::{HELLO_BYTES, Message},
ser::{HELLO, Message},
util::QueryParse,
};

Expand Down Expand Up @@ -94,33 +93,24 @@ async fn handler<'h>(req: Request<'h, db_unrealistic::Client>, res: Response<'h>
"/plaintext" => {
// unrealistic due to no body streaming and no post processing. violating middleware feature of xitca-web
res.status(StatusCode::OK)
// unrealistic due to no charset k/v pair
.header("content-type", "text/plain")
.header("server", "X")
// unrealistic content length header.
.header("content-length", "13")
.body_writer(|buf| buf.extend_from_slice(HELLO_BYTES))
.body(HELLO.as_bytes())
}
"/json" => res
.status(StatusCode::OK)
.header("content-type", "application/json")
.header("server", "X")
// unrealistic content length header.
.header("content-length", "27")
// snoic-rs crate is realistic approach to json serializer.
// That said xitca-web by default utilize serde-json as serializer making it an unrealistic representation of framework performance
.body_writer(|buf| sonic_rs::to_writer(buf.writer(), &Message::new()).unwrap()),
"/json" => json_response(res, Message::HELLO),

// all database related categories are unrealistic. please reference db_unrealistic module for detail.
"/fortunes" => {
// unrealistic due to no error handling. any db/serialization error will cause process crash.
// the same goes for all following unwraps on database related functions.
let fortunes = req.ctx.fortunes().await.unwrap().render_once().unwrap();
res.status(StatusCode::OK)
.header("content-type", "text/html; charset=utf-8")
.header("server", "X")
.body(fortunes.as_bytes())
}
"/db" => {
// unrealistic due to no error handling. any db/serialization error will cause process crash.
// the same goes for all following unwraps on database related functions.
let world = req.ctx.db().await.unwrap();
json_response(res, &world)
}
Expand All @@ -138,12 +128,10 @@ async fn handler<'h>(req: Request<'h, db_unrealistic::Client>, res: Response<'h>
}
}

fn json_response<'r, T>(res: Response<'r>, val: &T) -> Response<'r, 3>
where
T: serde_core::Serialize,
{
let mut buf = xitca_http::bytes::BytesMut::new();
sonic_rs::to_writer((&mut buf).writer(), val).unwrap();
fn json_response<'r>(res: Response<'r>, val: &impl serde_core::Serialize) -> Response<'r, 3> {
// snoic-rs crate is realistic approach to json serializer.
// That said xitca-web by default utilize serde-json as serializer making it an unrealistic representation of framework performance
let buf = ser::json_serialize(val).unwrap();
res.status(StatusCode::OK)
.header("content-type", "application/json")
.header("server", "X")
Expand Down
Loading
Loading