From 3f5d0985f04b3af26dd37f9cc3c74a44dd0dbfcf Mon Sep 17 00:00:00 2001 From: PhotonQuantum Date: Fri, 15 Oct 2021 07:12:53 +0800 Subject: [PATCH 1/2] Move ReadBuffer chunk to heap --- src/buffer.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/buffer.rs b/src/buffer.rs index c080fdb..a5e7490 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -12,7 +12,7 @@ use bytes::Buf; #[derive(Debug)] pub struct ReadBuffer { storage: Cursor>, - chunk: [u8; CHUNK_SIZE], + chunk: Box<[u8; CHUNK_SIZE]>, } impl ReadBuffer { @@ -28,7 +28,7 @@ impl ReadBuffer { /// Create a input buffer filled with previously read data. pub fn from_partially_read(part: Vec) -> Self { - Self { storage: Cursor::new(part), chunk: [0; CHUNK_SIZE] } + Self { storage: Cursor::new(part), chunk: Box::new([0; CHUNK_SIZE]) } } /// Get a cursor to the data storage. @@ -54,7 +54,7 @@ impl ReadBuffer { /// Read next portion of data from the given input stream. pub fn read_from(&mut self, stream: &mut S) -> IoResult { self.clean_up(); - let size = stream.read(&mut self.chunk)?; + let size = stream.read(&mut *self.chunk)?; self.storage.get_mut().extend_from_slice(&self.chunk[..size]); Ok(size) } From 76eaa8ac60747e011657b1e3dd52c1c1eb14076b Mon Sep 17 00:00:00 2001 From: PhotonQuantum Date: Fri, 15 Oct 2021 07:47:24 +0800 Subject: [PATCH 2/2] Add benchmark for stack read buffer --- benches/buffer.rs | 99 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/benches/buffer.rs b/benches/buffer.rs index eaf4e53..cf4e4fa 100644 --- a/benches/buffer.rs +++ b/benches/buffer.rs @@ -1,19 +1,107 @@ use std::io::{Cursor, Read}; +use std::io::Result as IoResult; +use bytes::Buf; use criterion::*; use input_buffer::InputBuffer; + use tungstenite::buffer::ReadBuffer; const CHUNK_SIZE: usize = 4096; +/// A FIFO buffer for reading packets from the network. +#[derive(Debug)] +pub struct StackReadBuffer { + storage: Cursor>, + chunk: [u8; CHUNK_SIZE], +} + +impl StackReadBuffer { + /// Create a new empty input buffer. + pub fn new() -> Self { + Self::with_capacity(CHUNK_SIZE) + } + + /// Create a new empty input buffer with a given `capacity`. + pub fn with_capacity(capacity: usize) -> Self { + Self::from_partially_read(Vec::with_capacity(capacity)) + } + + /// Create a input buffer filled with previously read data. + pub fn from_partially_read(part: Vec) -> Self { + Self { storage: Cursor::new(part), chunk: [0; CHUNK_SIZE] } + } + + /// Get a cursor to the data storage. + pub fn as_cursor(&self) -> &Cursor> { + &self.storage + } + + /// Get a cursor to the mutable data storage. + pub fn as_cursor_mut(&mut self) -> &mut Cursor> { + &mut self.storage + } + + /// Consume the `ReadBuffer` and get the internal storage. + pub fn into_vec(mut self) -> Vec { + // Current implementation of `tungstenite-rs` expects that the `into_vec()` drains + // the data from the container that has already been read by the cursor. + self.clean_up(); + + // Now we can safely return the internal container. + self.storage.into_inner() + } + + /// Read next portion of data from the given input stream. + pub fn read_from(&mut self, stream: &mut S) -> IoResult { + self.clean_up(); + let size = stream.read(&mut self.chunk)?; + self.storage.get_mut().extend_from_slice(&self.chunk[..size]); + Ok(size) + } + + /// Cleans ups the part of the vector that has been already read by the cursor. + fn clean_up(&mut self) { + let pos = self.storage.position() as usize; + self.storage.get_mut().drain(0..pos).count(); + self.storage.set_position(0); + } +} + +impl Buf for StackReadBuffer { + fn remaining(&self) -> usize { + Buf::remaining(self.as_cursor()) + } + + fn chunk(&self) -> &[u8] { + Buf::chunk(self.as_cursor()) + } + + fn advance(&mut self, cnt: usize) { + Buf::advance(self.as_cursor_mut(), cnt) + } +} + +impl Default for StackReadBuffer { + fn default() -> Self { + Self::new() + } +} + #[inline] -fn current_input_buffer(mut stream: impl Read) { +fn input_buffer(mut stream: impl Read) { let mut buffer = InputBuffer::with_capacity(CHUNK_SIZE); while buffer.read_from(&mut stream).unwrap() != 0 {} } #[inline] -fn fast_input_buffer(mut stream: impl Read) { +fn stack_read_buffer(mut stream: impl Read) { + let mut buffer = StackReadBuffer::::new(); + while buffer.read_from(&mut stream).unwrap() != 0 {} +} + +#[inline] +fn heap_read_buffer(mut stream: impl Read) { let mut buffer = ReadBuffer::::new(); while buffer.read_from(&mut stream).unwrap() != 0 {} } @@ -25,10 +113,9 @@ fn benchmark(c: &mut Criterion) { let mut group = c.benchmark_group("buffers"); group.throughput(Throughput::Bytes(STREAM_SIZE as u64)); - group.bench_function("InputBuffer", |b| { - b.iter(|| current_input_buffer(black_box(stream.clone()))) - }); - group.bench_function("ReadBuffer", |b| b.iter(|| fast_input_buffer(black_box(stream.clone())))); + group.bench_function("InputBuffer", |b| b.iter(|| input_buffer(black_box(stream.clone())))); + group.bench_function("ReadBuffer (stack)", |b| b.iter(|| stack_read_buffer(black_box(stream.clone())))); + group.bench_function("ReadBuffer (heap)", |b| b.iter(|| heap_read_buffer(black_box(stream.clone())))); group.finish(); }