Skip to content

Instantly share code, notes, and snippets.

@siddontang
Last active December 28, 2016 07:20
Show Gist options
  • Save siddontang/ef39b619b69376533eb5393b2d435feb to your computer and use it in GitHub Desktop.
Save siddontang/ef39b619b69376533eb5393b2d435feb to your computer and use it in GitHub Desktop.
benchmark outstream with buffer and no buffer
#![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