Merge pull request #241 from PhotonQuantum/master

Move ReadBuffer chunk to heap
pull/250/head
Daniel Abramov 3 years ago committed by GitHub
commit 54cca09538
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 99
      benches/buffer.rs
  2. 6
      src/buffer.rs

@ -1,19 +1,107 @@
use std::io::{Cursor, Read}; use std::io::{Cursor, Read};
use std::io::Result as IoResult;
use bytes::Buf;
use criterion::*; use criterion::*;
use input_buffer::InputBuffer; use input_buffer::InputBuffer;
use tungstenite::buffer::ReadBuffer; use tungstenite::buffer::ReadBuffer;
const CHUNK_SIZE: usize = 4096; const CHUNK_SIZE: usize = 4096;
/// A FIFO buffer for reading packets from the network.
#[derive(Debug)]
pub struct StackReadBuffer<const CHUNK_SIZE: usize> {
storage: Cursor<Vec<u8>>,
chunk: [u8; CHUNK_SIZE],
}
impl<const CHUNK_SIZE: usize> StackReadBuffer<CHUNK_SIZE> {
/// 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<u8>) -> Self {
Self { storage: Cursor::new(part), chunk: [0; CHUNK_SIZE] }
}
/// Get a cursor to the data storage.
pub fn as_cursor(&self) -> &Cursor<Vec<u8>> {
&self.storage
}
/// Get a cursor to the mutable data storage.
pub fn as_cursor_mut(&mut self) -> &mut Cursor<Vec<u8>> {
&mut self.storage
}
/// Consume the `ReadBuffer` and get the internal storage.
pub fn into_vec(mut self) -> Vec<u8> {
// 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<S: Read>(&mut self, stream: &mut S) -> IoResult<usize> {
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<const CHUNK_SIZE: usize> Buf for StackReadBuffer<CHUNK_SIZE> {
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<const CHUNK_SIZE: usize> Default for StackReadBuffer<CHUNK_SIZE> {
fn default() -> Self {
Self::new()
}
}
#[inline] #[inline]
fn current_input_buffer(mut stream: impl Read) { fn input_buffer(mut stream: impl Read) {
let mut buffer = InputBuffer::with_capacity(CHUNK_SIZE); let mut buffer = InputBuffer::with_capacity(CHUNK_SIZE);
while buffer.read_from(&mut stream).unwrap() != 0 {} while buffer.read_from(&mut stream).unwrap() != 0 {}
} }
#[inline] #[inline]
fn fast_input_buffer(mut stream: impl Read) { fn stack_read_buffer(mut stream: impl Read) {
let mut buffer = StackReadBuffer::<CHUNK_SIZE>::new();
while buffer.read_from(&mut stream).unwrap() != 0 {}
}
#[inline]
fn heap_read_buffer(mut stream: impl Read) {
let mut buffer = ReadBuffer::<CHUNK_SIZE>::new(); let mut buffer = ReadBuffer::<CHUNK_SIZE>::new();
while buffer.read_from(&mut stream).unwrap() != 0 {} while buffer.read_from(&mut stream).unwrap() != 0 {}
} }
@ -25,10 +113,9 @@ fn benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("buffers"); let mut group = c.benchmark_group("buffers");
group.throughput(Throughput::Bytes(STREAM_SIZE as u64)); group.throughput(Throughput::Bytes(STREAM_SIZE as u64));
group.bench_function("InputBuffer", |b| { group.bench_function("InputBuffer", |b| b.iter(|| input_buffer(black_box(stream.clone()))));
b.iter(|| current_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.bench_function("ReadBuffer", |b| b.iter(|| fast_input_buffer(black_box(stream.clone()))));
group.finish(); group.finish();
} }

@ -12,7 +12,7 @@ use bytes::Buf;
#[derive(Debug)] #[derive(Debug)]
pub struct ReadBuffer<const CHUNK_SIZE: usize> { pub struct ReadBuffer<const CHUNK_SIZE: usize> {
storage: Cursor<Vec<u8>>, storage: Cursor<Vec<u8>>,
chunk: [u8; CHUNK_SIZE], chunk: Box<[u8; CHUNK_SIZE]>,
} }
impl<const CHUNK_SIZE: usize> ReadBuffer<CHUNK_SIZE> { impl<const CHUNK_SIZE: usize> ReadBuffer<CHUNK_SIZE> {
@ -28,7 +28,7 @@ impl<const CHUNK_SIZE: usize> ReadBuffer<CHUNK_SIZE> {
/// Create a input buffer filled with previously read data. /// Create a input buffer filled with previously read data.
pub fn from_partially_read(part: Vec<u8>) -> Self { pub fn from_partially_read(part: Vec<u8>) -> 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. /// Get a cursor to the data storage.
@ -54,7 +54,7 @@ impl<const CHUNK_SIZE: usize> ReadBuffer<CHUNK_SIZE> {
/// Read next portion of data from the given input stream. /// Read next portion of data from the given input stream.
pub fn read_from<S: Read>(&mut self, stream: &mut S) -> IoResult<usize> { pub fn read_from<S: Read>(&mut self, stream: &mut S) -> IoResult<usize> {
self.clean_up(); 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]); self.storage.get_mut().extend_from_slice(&self.chunk[..size]);
Ok(size) Ok(size)
} }

Loading…
Cancel
Save