From d53c252da05277323870cca4d94876b63cdd803a Mon Sep 17 00:00:00 2001 From: Jose Quintana <1700322+joseluisq@users.noreply.github.com> Date: Tue, 10 Oct 2023 01:57:27 +0200 Subject: [PATCH] refactor: optimize buffer size for static file reads (#269) it reduces the RAM utilization by around 15% and increases slightly the req/sec average. https://gist.github.com/joseluisq/0b18c10d89fdf01beeb9e9b9b03cb761 --- src/static_files.rs | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/static_files.rs b/src/static_files.rs index 1875ee7..3e727fd 100644 --- a/src/static_files.rs +++ b/src/static_files.rs @@ -538,21 +538,45 @@ impl Conditionals { } #[cfg(unix)] -const READ_BUF_SIZE: usize = 4_096; +const DEFAULT_READ_BUF_SIZE: usize = 4_096; #[cfg(not(unix))] -const READ_BUF_SIZE: usize = 8_192; +const DEFAULT_READ_BUF_SIZE: usize = 8_192; + +fn optimal_buf_size(metadata: &Metadata) -> usize { + let block_size = get_block_size(metadata); + + // If file length is smaller than block size, + // don't waste space reserving a bigger-than-needed buffer. + std::cmp::min(block_size as u64, metadata.len()) as usize +} + +#[cfg(unix)] +fn get_block_size(metadata: &Metadata) -> usize { + use std::os::unix::fs::MetadataExt; + // TODO: blksize() returns u64, should handle bad cast... + // (really, a block size bigger than 4gb?) + + // Use device blocksize unless it's really small. + std::cmp::max(metadata.blksize() as usize, DEFAULT_READ_BUF_SIZE) +} + +#[cfg(not(unix))] +fn get_block_size(_metadata: &Metadata) -> usize { + DEFAULT_READ_BUF_SIZE +} #[derive(Debug)] struct FileStream { reader: T, + buf_size: usize, } impl Stream for FileStream { type Item = Result; fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - let mut buf = BytesMut::zeroed(READ_BUF_SIZE); + let mut buf = BytesMut::zeroed(self.buf_size); match Pin::into_inner(self).reader.read(&mut buf[..]) { Ok(n) => { if n == 0 { @@ -579,6 +603,7 @@ async fn response_body( match conditionals.check(modified) { Cond::NoBody(resp) => Ok(resp), Cond::WithBody(range) => { + let buf_size = optimal_buf_size(meta); bytes_range(range, len) .map(|(start, end)| { match file.seek(SeekFrom::Start(start)) { @@ -591,7 +616,7 @@ async fn response_body( let sub_len = end - start; let reader = BufReader::new(file).take(sub_len); - let stream = FileStream { reader }; + let stream = FileStream { reader, buf_size }; let body = Body::wrap_stream(stream); let mut resp = Response::new(body); -- libgit2 1.7.2