index : static-web-server.git

ascending towards madness

author Jose Quintana <1700322+joseluisq@users.noreply.github.com> 2023-10-09 23:57:27.0 +00:00:00
committer GitHub <noreply@github.com> 2023-10-09 23:57:27.0 +00:00:00
commit
d53c252da05277323870cca4d94876b63cdd803a [patch]
tree
800beb83cd3b86725d074dae3c6738aa542127f0
parent
efb2c0cd8a14391c615cf1e6314fc82f3d8b0b39
download
d53c252da05277323870cca4d94876b63cdd803a.tar.gz

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

Diff

 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<T> {
    reader: T,
    buf_size: usize,
}

impl<T: Read + Unpin> Stream for FileStream<T> {
    type Item = Result<Bytes>;

    fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        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);