From 3c1e4dd0087cf3c4ed6c772d5f93a7cf654d9e13 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 8 Jan 2026 20:29:57 +0100 Subject: [PATCH 1/2] ntex: optimize allocations --- frameworks/Rust/ntex/Cargo.toml | 14 ++++----- frameworks/Rust/ntex/src/db.rs | 46 +++++++++++++++++------------- frameworks/Rust/ntex/src/utils.rs | 47 ++++++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 29 deletions(-) diff --git a/frameworks/Rust/ntex/Cargo.toml b/frameworks/Rust/ntex/Cargo.toml index ae3e4cc977b..2f39f875024 100755 --- a/frameworks/Rust/ntex/Cargo.toml +++ b/frameworks/Rust/ntex/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ntex-bench" +name = "ntex-framework" version = "3.0.0" edition = "2018" @@ -60,17 +60,15 @@ tokio = ["ntex/tokio"] # compio runtime compio = ["ntex/compio"] -# neon runtime -neon = ["ntex/neon"] +# neon polling runtime +neon = ["ntex/neon-polling"] -# neon runtime +# neon io-uring runtime neon-uring = ["ntex/neon-uring"] [dependencies] -ntex = "3.0.0-pre.5" -ntex-neon = "0.1.35" -ntex-net = "3.0.0" -ntex-bytes = { version = "1", features=["simd"] } +ntex = "3.0.0-pre.10" +ntex-bytes = { version = "1.2", features=["simd"] } mimalloc = { version = "0.1.25", default-features = false } snmalloc-rs = { version = "0.3.3", features = ["native-cpu"] } yarte = { version = "0.15", features = ["bytes-buf", "json"] } diff --git a/frameworks/Rust/ntex/src/db.rs b/frameworks/Rust/ntex/src/db.rs index 0df7bf14857..3572075649c 100644 --- a/frameworks/Rust/ntex/src/db.rs +++ b/frameworks/Rust/ntex/src/db.rs @@ -1,7 +1,7 @@ -use std::cell::RefCell; +use std::cell::Cell; use nanorand::{Rng, WyRand}; -use ntex::util::{Bytes, BytesMut}; +use ntex::util::{Bytes, BytesVec, BufMut}; use smallvec::SmallVec; use tokio_postgres::{connect, Client, Statement}; use yarte::TemplateBytesTrait; @@ -33,7 +33,7 @@ pub struct PgConnection { world: Statement, rng: WyRand, updates: Statement, - buf: RefCell, + buf: Cell>, } impl PgConnection { @@ -55,7 +55,7 @@ impl PgConnection { world, updates, rng: WyRand::new(), - buf: RefCell::new(BytesMut::with_capacity(10 * 1024 * 1024)), + buf: Cell::new(Some(BytesVec::with_capacity(10 * 1024 * 1024))), } } } @@ -66,17 +66,18 @@ impl PgConnection { let row = self.cl.query_one(&self.world, &[&random_id]).await.unwrap(); - let mut body = self.buf.borrow_mut(); - utils::reserve(&mut body, 1024); + let mut body = self.buf.take().unwrap(); sonic_rs::to_writer( - utils::BytesWriter(&mut body), + utils::BVecWriter::new(&mut body), &World { id: row.get(0), randomnumber: row.get(1), }, ) .unwrap(); - body.split().freeze() + let result = body.take_bytes(); + self.buf.set(Some(body)); + result } pub async fn get_worlds(&self, num: usize) -> Bytes { @@ -96,10 +97,11 @@ impl PgConnection { }) } - let mut body = self.buf.borrow_mut(); - utils::reserve(&mut body, 2 * 1024); - sonic_rs::to_writer(utils::BytesWriter(&mut body), &worlds[..]).unwrap(); - body.split().freeze() + let mut body = self.buf.take().unwrap(); + sonic_rs::to_writer(utils::BVecWriter::new(&mut body), &worlds[..]).unwrap(); + let result = body.take_bytes(); + self.buf.set(Some(body)); + result } pub async fn update(&self, num: usize) -> Bytes { @@ -131,10 +133,11 @@ impl PgConnection { update.await.unwrap(); - let mut body = self.buf.borrow_mut(); - utils::reserve(&mut body, 2 * 1024); - sonic_rs::to_writer(utils::BytesWriter(&mut body), &worlds[..]).unwrap(); - body.split().freeze() + let mut body = self.buf.take().unwrap(); + sonic_rs::to_writer(utils::BVecWriter::new(&mut body), &worlds[..]).unwrap(); + let result = body.take_bytes(); + self.buf.set(Some(body)); + result } pub async fn tell_fortune(&self) -> Bytes { @@ -151,8 +154,11 @@ impl PgConnection { })); fortunes.sort_by(|it, next| it.message.cmp(&next.message)); - let mut body = std::mem::replace(&mut *self.buf.borrow_mut(), BytesMut::new()); - utils::reserve(&mut body, 4 * 1024); + let mut body = self.buf.take().unwrap(); + let remaining = body.remaining_mut(); + if remaining < 4 * 1024 { + body.reserve(128 * 1024); + } FortunesTemplate { fortunes: &fortunes, @@ -160,8 +166,8 @@ impl PgConnection { .write_call(&mut body); fortunes.clear(); - let result = body.split().freeze(); - let _ = std::mem::replace(&mut *self.buf.borrow_mut(), body); + let result = body.take_bytes(); + self.buf.set(Some(body)); result } } diff --git a/frameworks/Rust/ntex/src/utils.rs b/frameworks/Rust/ntex/src/utils.rs index b3399bd8497..4b7759f7230 100644 --- a/frameworks/Rust/ntex/src/utils.rs +++ b/frameworks/Rust/ntex/src/utils.rs @@ -3,7 +3,8 @@ use std::{cmp, io, io::Write, mem::MaybeUninit, slice::from_raw_parts_mut}; use atoi::FromRadix10; use ntex::http::{header::HeaderValue, HttpServiceConfig, KeepAlive}; -use ntex::{io::IoConfig, time::Seconds, util::BufMut, util::Bytes, util::BytesMut, SharedCfg}; +use ntex::util::{BufMut, Bytes, BytesMut, BytesVec}; +use ntex::{io::IoConfig, time::Seconds, SharedCfg}; use sonic_rs::writer::WriteExt; pub const HDR_SERVER: HeaderValue = HeaderValue::from_static("N"); @@ -97,3 +98,47 @@ impl WriteExt for BytesWriter<'_> { Ok(()) } } + +pub struct BVecWriter<'a>(&'a mut BytesVec); + +impl<'a> BVecWriter<'a> { + pub fn new(buf: &'a mut BytesVec) -> BVecWriter<'a> { + let remaining = buf.remaining_mut(); + if remaining < 2048 { + buf.reserve(HW); + } + Self(buf) + } +} + +impl Write for BVecWriter<'_> { + fn write(&mut self, src: &[u8]) -> Result { + self.0.extend_from_slice(src); + Ok(src.len()) + } + + fn flush(&mut self) -> Result<(), io::Error> { + Ok(()) + } +} + +impl WriteExt for BVecWriter<'_> { + #[inline(always)] + fn reserve_with(&mut self, additional: usize) -> Result<&mut [MaybeUninit], io::Error> { + self.0.reserve(additional); + + unsafe { + let ptr = self.0.as_mut_ptr().add(self.0.len()) as *mut MaybeUninit; + Ok(from_raw_parts_mut(ptr, additional)) + } + } + + #[inline(always)] + unsafe fn flush_len(&mut self, additional: usize) -> io::Result<()> { + unsafe { + let new_len = self.0.len() + additional; + self.0.set_len(new_len); + } + Ok(()) + } +} From ccf42afaccaf75db2bd2aa72018c1f440ff6570e Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 8 Jan 2026 21:21:36 +0100 Subject: [PATCH 2/2] wip --- frameworks/Rust/ntex/Cargo.toml | 2 +- frameworks/Rust/ntex/src/db.rs | 8 ++--- frameworks/Rust/ntex/src/main.rs | 8 ++--- frameworks/Rust/ntex/src/main_plt.rs | 48 +++++++++++++--------------- frameworks/Rust/ntex/src/utils.rs | 45 +++----------------------- 5 files changed, 34 insertions(+), 77 deletions(-) diff --git a/frameworks/Rust/ntex/Cargo.toml b/frameworks/Rust/ntex/Cargo.toml index 2f39f875024..884d962fd6c 100755 --- a/frameworks/Rust/ntex/Cargo.toml +++ b/frameworks/Rust/ntex/Cargo.toml @@ -67,7 +67,7 @@ neon = ["ntex/neon-polling"] neon-uring = ["ntex/neon-uring"] [dependencies] -ntex = "3.0.0-pre.10" +ntex = "3.0.0-pre.11" ntex-bytes = { version = "1.2", features=["simd"] } mimalloc = { version = "0.1.25", default-features = false } snmalloc-rs = { version = "0.3.3", features = ["native-cpu"] } diff --git a/frameworks/Rust/ntex/src/db.rs b/frameworks/Rust/ntex/src/db.rs index 3572075649c..fa7af84ff1c 100644 --- a/frameworks/Rust/ntex/src/db.rs +++ b/frameworks/Rust/ntex/src/db.rs @@ -1,7 +1,7 @@ use std::cell::Cell; use nanorand::{Rng, WyRand}; -use ntex::util::{Bytes, BytesVec, BufMut}; +use ntex::util::{Bytes, BytesVec}; use smallvec::SmallVec; use tokio_postgres::{connect, Client, Statement}; use yarte::TemplateBytesTrait; @@ -155,11 +155,7 @@ impl PgConnection { fortunes.sort_by(|it, next| it.message.cmp(&next.message)); let mut body = self.buf.take().unwrap(); - let remaining = body.remaining_mut(); - if remaining < 4 * 1024 { - body.reserve(128 * 1024); - } - + utils::reserve(&mut body, 4 * 1024); FortunesTemplate { fortunes: &fortunes, } diff --git a/frameworks/Rust/ntex/src/main.rs b/frameworks/Rust/ntex/src/main.rs index 23494837277..fda38550e4e 100644 --- a/frameworks/Rust/ntex/src/main.rs +++ b/frameworks/Rust/ntex/src/main.rs @@ -2,7 +2,7 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; use ntex::http::header::{CONTENT_TYPE, SERVER}; -use ntex::{http, util::BytesMut, web}; +use ntex::{http, util::BytesVec, web}; use sonic_rs::Serialize; mod utils; @@ -14,16 +14,16 @@ pub struct Message { #[web::get("/json")] async fn json() -> web::HttpResponse { - let mut body = BytesMut::with_capacity(utils::SIZE); + let mut body = BytesVec::with_capacity(utils::SIZE); sonic_rs::to_writer( - utils::BytesWriter(&mut body), + utils::BVecWriter(&mut body), &Message { message: "Hello, World!", }, ) .unwrap(); - let mut response = web::HttpResponse::with_body(http::StatusCode::OK, body.into()); + let mut response = web::HttpResponse::with_body(http::StatusCode::OK, body.freeze().into()); response.headers_mut().insert(SERVER, utils::HDR_SERVER); response .headers_mut() diff --git a/frameworks/Rust/ntex/src/main_plt.rs b/frameworks/Rust/ntex/src/main_plt.rs index 83619c5053c..0f93d1ec30f 100644 --- a/frameworks/Rust/ntex/src/main_plt.rs +++ b/frameworks/Rust/ntex/src/main_plt.rs @@ -3,7 +3,7 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; use std::{future::Future, io, pin::Pin, task::ready, task::Context, task::Poll}; -use ntex::{fn_service, http::h1, io::Io, io::RecvError}; +use ntex::{fn_service, http::h1, http::DateService, io::Io, io::RecvError}; use sonic_rs::Serialize; mod utils; @@ -35,32 +35,30 @@ impl Future for App { match ready!(this.io.poll_recv(&this.codec, cx)) { Ok((req, _)) => { let _ = this.io.with_write_buf(|buf| { - buf.with_bytes_mut(|buf| { - utils::reserve(buf, 2 * 1024); - match req.path() { - "/json" => { - buf.extend_from_slice(JSON); - this.codec.set_date_header(buf); + utils::reserve(buf, 2 * 1024); + match req.path() { + "/json" => { + buf.extend_from_slice(JSON); + DateService.bset_date_header(buf); - sonic_rs::to_writer( - utils::BytesWriter(buf), - &Message { - message: "Hello, World!", - }, - ) - .unwrap(); - } - "/plaintext" => { - buf.extend_from_slice(PLAIN); - this.codec.set_date_header(buf); - buf.extend_from_slice(BODY); - } - _ => { - buf.extend_from_slice(HTTPNFOUND); - buf.extend_from_slice(HDR_SERVER); - } + sonic_rs::to_writer( + utils::BVecWriter(buf), + &Message { + message: "Hello, World!", + }, + ) + .unwrap(); } - }) + "/plaintext" => { + buf.extend_from_slice(PLAIN); + DateService.bset_date_header(buf); + buf.extend_from_slice(BODY); + } + _ => { + buf.extend_from_slice(HTTPNFOUND); + buf.extend_from_slice(HDR_SERVER); + } + } }); } Err(RecvError::WriteBackpressure) => { diff --git a/frameworks/Rust/ntex/src/utils.rs b/frameworks/Rust/ntex/src/utils.rs index 4b7759f7230..8f4aa0f2d36 100644 --- a/frameworks/Rust/ntex/src/utils.rs +++ b/frameworks/Rust/ntex/src/utils.rs @@ -3,7 +3,7 @@ use std::{cmp, io, io::Write, mem::MaybeUninit, slice::from_raw_parts_mut}; use atoi::FromRadix10; use ntex::http::{header::HeaderValue, HttpServiceConfig, KeepAlive}; -use ntex::util::{BufMut, Bytes, BytesMut, BytesVec}; +use ntex::util::{BufMut, Bytes, BytesVec}; use ntex::{io::IoConfig, time::Seconds, SharedCfg}; use sonic_rs::writer::WriteExt; @@ -58,55 +58,18 @@ pub fn get_query_param(query: Option<&str>) -> usize { cmp::min(500, cmp::max(1, q) as usize) } -pub fn reserve(buf: &mut BytesMut, lw: usize) { +pub fn reserve(buf: &mut BytesVec, lw: usize) { let remaining = buf.remaining_mut(); if remaining < lw { buf.reserve(HW); } } -pub struct BytesWriter<'a>(pub &'a mut BytesMut); - -impl Write for BytesWriter<'_> { - fn write(&mut self, src: &[u8]) -> Result { - self.0.extend_from_slice(src); - Ok(src.len()) - } - - fn flush(&mut self) -> Result<(), io::Error> { - Ok(()) - } -} - -impl WriteExt for BytesWriter<'_> { - #[inline(always)] - fn reserve_with(&mut self, additional: usize) -> Result<&mut [MaybeUninit], io::Error> { - self.0.reserve(additional); - - unsafe { - let ptr = self.0.as_mut_ptr().add(self.0.len()) as *mut MaybeUninit; - Ok(from_raw_parts_mut(ptr, additional)) - } - } - - #[inline(always)] - unsafe fn flush_len(&mut self, additional: usize) -> io::Result<()> { - unsafe { - let new_len = self.0.len() + additional; - self.0.set_len(new_len); - } - Ok(()) - } -} - -pub struct BVecWriter<'a>(&'a mut BytesVec); +pub struct BVecWriter<'a>(pub &'a mut BytesVec); impl<'a> BVecWriter<'a> { pub fn new(buf: &'a mut BytesVec) -> BVecWriter<'a> { - let remaining = buf.remaining_mut(); - if remaining < 2048 { - buf.reserve(HW); - } + reserve(buf, 2048); Self(buf) } }