Last active
December 28, 2016 07:20
-
-
Save siddontang/ef39b619b69376533eb5393b2d435feb to your computer and use it in GitHub Desktop.
benchmark outstream with buffer and no buffer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![allow(stable_features)] | |
#![feature(test)] | |
extern crate test; | |
use std::error::Error; | |
use std::fmt; | |
use std::io; | |
use std::io::Write; | |
use std::ptr; | |
pub const OUTPUT_STREAM_BUFFER_SIZE: usize = 8 * 1024; | |
pub type ProtobufResult<T> = Result<T, ProtobufError>; | |
#[derive(Debug)] | |
pub enum ProtobufError { | |
IoError(io::Error), | |
WireError(String), | |
MessageNotInitialized { message: &'static str }, | |
} | |
impl ProtobufError { | |
pub fn message_not_initialized(message: &'static str) -> ProtobufError { | |
ProtobufError::MessageNotInitialized { | |
message: message | |
} | |
} | |
} | |
impl fmt::Display for ProtobufError { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
fmt::Debug::fmt(self, f) | |
} | |
} | |
impl Error for ProtobufError { | |
fn description(&self) -> &str { | |
match self { | |
// not sure that cause should be included in message | |
&ProtobufError::IoError(ref e) => e.description(), | |
&ProtobufError::WireError(ref e) => &e, | |
&ProtobufError::MessageNotInitialized { .. } => "not all message fields set", | |
} | |
} | |
fn cause(&self) -> Option<&Error> { | |
match self { | |
&ProtobufError::IoError(ref e) => Some(e), | |
&ProtobufError::WireError(..) => None, | |
&ProtobufError::MessageNotInitialized { .. } => None, | |
} | |
} | |
} | |
impl From<io::Error> for ProtobufError { | |
fn from(err: io::Error) -> Self { | |
ProtobufError::IoError(err) | |
} | |
} | |
pub struct CodedOutputStream<'a> { | |
buffer: Box<[u8]>, | |
// within buffer | |
position: u32, | |
writer: &'a mut Write, | |
} | |
impl<'a> CodedOutputStream<'a> { | |
pub fn new(writer: &'a mut Write) -> CodedOutputStream<'a> { | |
let buffer_len = OUTPUT_STREAM_BUFFER_SIZE; | |
let mut buffer = Vec::with_capacity(buffer_len); | |
unsafe { buffer.set_len(buffer_len); } | |
CodedOutputStream { | |
buffer: buffer.into_boxed_slice(), | |
position: 0, | |
writer: writer, | |
} | |
} | |
fn refresh_buffer(&mut self) -> ProtobufResult<()> { | |
try!(self.writer.write_all(&self.buffer[0..self.position as usize])); | |
self.position = 0; | |
Ok(()) | |
} | |
pub fn flush(&mut self) -> ProtobufResult<()> { | |
try!(self.refresh_buffer()); | |
Ok(()) | |
} | |
pub fn write_raw_byte(&mut self, byte: u8) -> ProtobufResult<()> { | |
if self.position as usize == self.buffer.len() { | |
try!(self.refresh_buffer()); | |
} | |
self.buffer[self.position as usize] = byte; | |
self.position += 1; | |
Ok(()) | |
} | |
pub fn write_raw_bytes(&mut self, bytes: &[u8]) -> ProtobufResult<()> { | |
if bytes.len() <= self.buffer.len() - self.position as usize { | |
// TODO: use `copy_from_slice` as soon as rust 1.9 released | |
unsafe { | |
let dest = &mut self.buffer[self.position as usize..]; | |
ptr::copy_nonoverlapping(bytes.as_ptr(), dest.as_mut_ptr(), bytes.len()); | |
self.position += bytes.len() as u32; | |
return Ok(()); | |
} | |
} | |
try!(self.refresh_buffer()); | |
try!(self.writer.write_all(bytes)); | |
Ok(()) | |
} | |
} | |
pub struct NoBufferCodedOutputStream<'a> { | |
writer: &'a mut Write, | |
} | |
impl<'a> NoBufferCodedOutputStream<'a> { | |
pub fn new(writer: &'a mut Write) -> NoBufferCodedOutputStream<'a> { | |
NoBufferCodedOutputStream { | |
writer: writer, | |
} | |
} | |
pub fn flush(&mut self) -> ProtobufResult<()> { | |
try!(self.writer.flush()); | |
Ok(()) | |
} | |
pub fn write_raw_byte(&mut self, byte: u8) -> ProtobufResult<()> { | |
try!(self.writer.write_all(&[byte])); | |
Ok(()) | |
} | |
pub fn write_raw_bytes(&mut self, bytes: &[u8]) -> ProtobufResult<()> { | |
try!(self.writer.write_all(bytes)); | |
Ok(()) | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
use test::Bencher; | |
#[inline] | |
fn buffer_write_byte(os: &mut CodedOutputStream) { | |
for i in 0..10 { | |
os.write_raw_byte(i as u8).unwrap(); | |
} | |
os.flush().unwrap(); | |
} | |
#[inline] | |
fn buffer_write_bytes(os: &mut CodedOutputStream) { | |
for _ in 0..10 { | |
os.write_raw_bytes(b"1234567890").unwrap(); | |
} | |
os.flush().unwrap(); | |
} | |
#[inline] | |
fn no_buffer_write_byte(os: &mut NoBufferCodedOutputStream) { | |
for i in 0..10 { | |
os.write_raw_byte(i as u8).unwrap(); | |
} | |
os.flush().unwrap(); | |
} | |
#[inline] | |
fn no_buffer_write_bytes(os: &mut NoBufferCodedOutputStream) { | |
for _ in 0..10 { | |
os.write_raw_bytes(b"1234567890").unwrap(); | |
} | |
os.flush().unwrap(); | |
} | |
#[bench] | |
fn bench_buffer(b: &mut Bencher) { | |
b.iter(|| { | |
let mut v = Vec::new(); | |
let mut os = CodedOutputStream::new(&mut v); | |
buffer_write_byte(&mut os); | |
}); | |
} | |
#[bench] | |
fn bench_buffer_bytes(b: &mut Bencher) { | |
b.iter(|| { | |
let mut v = Vec::new(); | |
let mut os = CodedOutputStream::new(&mut v); | |
buffer_write_bytes(&mut os); | |
}); | |
} | |
#[bench] | |
fn bench_no_buffer(b: &mut Bencher) { | |
b.iter(|| { | |
let mut v = Vec::with_capacity(OUTPUT_STREAM_BUFFER_SIZE); | |
let mut os = NoBufferCodedOutputStream::new(&mut v); | |
no_buffer_write_byte(&mut os); | |
}); | |
} | |
#[bench] | |
fn bench_no_buffer_bytes(b: &mut Bencher) { | |
b.iter(|| { | |
let mut v = Vec::with_capacity(OUTPUT_STREAM_BUFFER_SIZE); | |
let mut os = NoBufferCodedOutputStream::new(&mut v); | |
no_buffer_write_bytes(&mut os); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment