From 0f39dc7b78fb5e6d79ab74756de2e717e6c19058 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Fri, 20 Jun 2014 23:05:42 -0400 Subject: [PATCH] std: inline many of the Writer/Reader methods This allows llvm to optimize away much of the overhead from using the MemReader/MemWriters. My benchmarks showed it to shave 15% off of my in progress serialization/json encoding. --- src/libstd/io/mem.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++ src/libstd/io/mod.rs | 37 ++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/src/libstd/io/mem.rs b/src/libstd/io/mem.rs index 5eca5361835..ec3d9370833 100644 --- a/src/libstd/io/mem.rs +++ b/src/libstd/io/mem.rs @@ -59,11 +59,13 @@ pub struct MemWriter { impl MemWriter { /// Create a new `MemWriter`. + #[inline] pub fn new() -> MemWriter { MemWriter::with_capacity(128) } /// Create a new `MemWriter`, allocating at least `n` bytes for /// the internal buffer. + #[inline] pub fn with_capacity(n: uint) -> MemWriter { MemWriter { buf: Vec::with_capacity(n), pos: 0 } } @@ -73,13 +75,16 @@ pub fn with_capacity(n: uint) -> MemWriter { /// /// No method is exposed for acquiring a mutable reference to the buffer /// because it could corrupt the state of this `MemWriter`. + #[inline] pub fn get_ref<'a>(&'a self) -> &'a [u8] { self.buf.as_slice() } /// Unwraps this `MemWriter`, returning the underlying buffer + #[inline] pub fn unwrap(self) -> Vec { self.buf } } impl Writer for MemWriter { + #[inline] fn write(&mut self, buf: &[u8]) -> IoResult<()> { // Make sure the internal buffer is as least as big as where we // currently are @@ -112,7 +117,10 @@ fn write(&mut self, buf: &[u8]) -> IoResult<()> { } impl Seek for MemWriter { + #[inline] fn tell(&self) -> IoResult { Ok(self.pos as u64) } + + #[inline] fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { let new = try!(combine(style, self.pos, self.buf.len(), pos)); self.pos = new as uint; @@ -140,6 +148,7 @@ pub struct MemReader { impl MemReader { /// Creates a new `MemReader` which will read the buffer given. The buffer /// can be re-acquired through `unwrap` + #[inline] pub fn new(buf: Vec) -> MemReader { MemReader { buf: buf, @@ -150,6 +159,7 @@ pub fn new(buf: Vec) -> MemReader { /// Tests whether this reader has read all bytes in its buffer. /// /// If `true`, then this will no longer return bytes from `read`. + #[inline] pub fn eof(&self) -> bool { self.pos >= self.buf.len() } /// Acquires an immutable reference to the underlying buffer of this @@ -157,13 +167,16 @@ pub fn eof(&self) -> bool { self.pos >= self.buf.len() } /// /// No method is exposed for acquiring a mutable reference to the buffer /// because it could corrupt the state of this `MemReader`. + #[inline] pub fn get_ref<'a>(&'a self) -> &'a [u8] { self.buf.as_slice() } /// Unwraps this `MemReader`, returning the underlying buffer + #[inline] pub fn unwrap(self) -> Vec { self.buf } } impl Reader for MemReader { + #[inline] fn read(&mut self, buf: &mut [u8]) -> IoResult { if self.eof() { return Err(io::standard_error(io::EndOfFile)) } @@ -182,7 +195,10 @@ fn read(&mut self, buf: &mut [u8]) -> IoResult { } impl Seek for MemReader { + #[inline] fn tell(&self) -> IoResult { Ok(self.pos as u64) } + + #[inline] fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { let new = try!(combine(style, self.pos, self.buf.len(), pos)); self.pos = new as uint; @@ -191,6 +207,7 @@ fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { } impl Buffer for MemReader { + #[inline] fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { if self.pos < self.buf.len() { Ok(self.buf.slice_from(self.pos)) @@ -198,6 +215,8 @@ fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { Err(io::standard_error(io::EndOfFile)) } } + + #[inline] fn consume(&mut self, amt: uint) { self.pos += amt; } } @@ -227,6 +246,7 @@ pub struct BufWriter<'a> { impl<'a> BufWriter<'a> { /// Creates a new `BufWriter` which will wrap the specified buffer. The /// writer initially starts at position 0. + #[inline] pub fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> { BufWriter { buf: buf, @@ -236,6 +256,7 @@ pub fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> { } impl<'a> Writer for BufWriter<'a> { + #[inline] fn write(&mut self, buf: &[u8]) -> IoResult<()> { // return an error if the entire write does not fit in the buffer let max_size = self.buf.len(); @@ -254,7 +275,10 @@ fn write(&mut self, buf: &[u8]) -> IoResult<()> { } impl<'a> Seek for BufWriter<'a> { + #[inline] fn tell(&self) -> IoResult { Ok(self.pos as u64) } + + #[inline] fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { let new = try!(combine(style, self.pos, self.buf.len(), pos)); self.pos = new as uint; @@ -282,6 +306,7 @@ pub struct BufReader<'a> { impl<'a> BufReader<'a> { /// Creates a new buffered reader which will read the specified buffer + #[inline] pub fn new<'a>(buf: &'a [u8]) -> BufReader<'a> { BufReader { buf: buf, @@ -292,10 +317,12 @@ pub fn new<'a>(buf: &'a [u8]) -> BufReader<'a> { /// Tests whether this reader has read all bytes in its buffer. /// /// If `true`, then this will no longer return bytes from `read`. + #[inline] pub fn eof(&self) -> bool { self.pos >= self.buf.len() } } impl<'a> Reader for BufReader<'a> { + #[inline] fn read(&mut self, buf: &mut [u8]) -> IoResult { if self.eof() { return Err(io::standard_error(io::EndOfFile)) } @@ -314,7 +341,10 @@ fn read(&mut self, buf: &mut [u8]) -> IoResult { } impl<'a> Seek for BufReader<'a> { + #[inline] fn tell(&self) -> IoResult { Ok(self.pos as u64) } + + #[inline] fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { let new = try!(combine(style, self.pos, self.buf.len(), pos)); self.pos = new as uint; @@ -323,6 +353,7 @@ fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { } impl<'a> Buffer for BufReader<'a> { + #[inline] fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { if self.pos < self.buf.len() { Ok(self.buf.slice_from(self.pos)) @@ -330,15 +361,19 @@ fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { Err(io::standard_error(io::EndOfFile)) } } + + #[inline] fn consume(&mut self, amt: uint) { self.pos += amt; } } #[cfg(test)] mod test { + extern crate test; use prelude::*; use super::*; use io::*; use io; + use self::test::Bencher; use str::StrSlice; #[test] @@ -571,4 +606,59 @@ fn io_read_at_least() { assert!(r.read_at_least(buf.len(), buf).is_err()); assert_eq!(buf.as_slice(), &[7, 8, 6]); } + + #[bench] + fn bench_mem_writer(b: &mut Bencher) { + b.iter(|| { + let mut wr = MemWriter::new(); + for _i in range(0, 10) { + wr.write([5, .. 10]).unwrap(); + } + assert_eq!(wr.unwrap().as_slice(), [5, .. 100].as_slice()); + }); + } + + #[bench] + fn bench_mem_reader(b: &mut Bencher) { + b.iter(|| { + let buf = Vec::from_slice([5 as u8, ..100]); + { + let mut rdr = MemReader::new(buf); + for _i in range(0, 10) { + let mut buf = [0 as u8, .. 10]; + rdr.read(buf).unwrap(); + assert_eq!(buf.as_slice(), [5, .. 10].as_slice()); + } + } + }); + } + + #[bench] + fn bench_buf_writer(b: &mut Bencher) { + b.iter(|| { + let mut buf = [0 as u8, ..100]; + { + let mut wr = BufWriter::new(buf); + for _i in range(0, 10) { + wr.write([5, .. 10]).unwrap(); + } + } + assert_eq!(buf.as_slice(), [5, .. 100].as_slice()); + }); + } + + #[bench] + fn bench_buf_reader(b: &mut Bencher) { + b.iter(|| { + let buf = [5 as u8, ..100]; + { + let mut rdr = BufReader::new(buf); + for _i in range(0, 10) { + let mut buf = [0 as u8, .. 10]; + rdr.read(buf).unwrap(); + assert_eq!(buf.as_slice(), [5, .. 10].as_slice()); + } + } + }); + } } diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index d9755cdce1a..1d6ad7c31e2 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -1084,6 +1084,7 @@ fn write(&mut self, bytes: &[u8]) -> fmt::Result { /// If other encodings are desired, it is recommended to compose this stream /// with another performing the conversion, or to use `write` with a /// converted byte-array instead. + #[inline] fn write_str(&mut self, s: &str) -> IoResult<()> { self.write(s.as_bytes()) } @@ -1095,11 +1096,13 @@ fn write_str(&mut self, s: &str) -> IoResult<()> { /// /// If other encodings or line ending flavors are desired, it is recommended /// that the `write` method is used specifically instead. + #[inline] fn write_line(&mut self, s: &str) -> IoResult<()> { self.write_str(s).and_then(|()| self.write(['\n' as u8])) } /// Write a single char, encoded as UTF-8. + #[inline] fn write_char(&mut self, c: char) -> IoResult<()> { let mut buf = [0u8, ..4]; let n = c.encode_utf8(buf.as_mut_slice()); @@ -1107,66 +1110,79 @@ fn write_char(&mut self, c: char) -> IoResult<()> { } /// Write the result of passing n through `int::to_str_bytes`. + #[inline] fn write_int(&mut self, n: int) -> IoResult<()> { write!(self, "{:d}", n) } /// Write the result of passing n through `uint::to_str_bytes`. + #[inline] fn write_uint(&mut self, n: uint) -> IoResult<()> { write!(self, "{:u}", n) } /// Write a little-endian uint (number of bytes depends on system). + #[inline] fn write_le_uint(&mut self, n: uint) -> IoResult<()> { extensions::u64_to_le_bytes(n as u64, uint::BYTES, |v| self.write(v)) } /// Write a little-endian int (number of bytes depends on system). + #[inline] fn write_le_int(&mut self, n: int) -> IoResult<()> { extensions::u64_to_le_bytes(n as u64, int::BYTES, |v| self.write(v)) } /// Write a big-endian uint (number of bytes depends on system). + #[inline] fn write_be_uint(&mut self, n: uint) -> IoResult<()> { extensions::u64_to_be_bytes(n as u64, uint::BYTES, |v| self.write(v)) } /// Write a big-endian int (number of bytes depends on system). + #[inline] fn write_be_int(&mut self, n: int) -> IoResult<()> { extensions::u64_to_be_bytes(n as u64, int::BYTES, |v| self.write(v)) } /// Write a big-endian u64 (8 bytes). + #[inline] fn write_be_u64(&mut self, n: u64) -> IoResult<()> { extensions::u64_to_be_bytes(n, 8u, |v| self.write(v)) } /// Write a big-endian u32 (4 bytes). + #[inline] fn write_be_u32(&mut self, n: u32) -> IoResult<()> { extensions::u64_to_be_bytes(n as u64, 4u, |v| self.write(v)) } /// Write a big-endian u16 (2 bytes). + #[inline] fn write_be_u16(&mut self, n: u16) -> IoResult<()> { extensions::u64_to_be_bytes(n as u64, 2u, |v| self.write(v)) } /// Write a big-endian i64 (8 bytes). + #[inline] fn write_be_i64(&mut self, n: i64) -> IoResult<()> { extensions::u64_to_be_bytes(n as u64, 8u, |v| self.write(v)) } /// Write a big-endian i32 (4 bytes). + #[inline] fn write_be_i32(&mut self, n: i32) -> IoResult<()> { extensions::u64_to_be_bytes(n as u64, 4u, |v| self.write(v)) } /// Write a big-endian i16 (2 bytes). + #[inline] fn write_be_i16(&mut self, n: i16) -> IoResult<()> { extensions::u64_to_be_bytes(n as u64, 2u, |v| self.write(v)) } /// Write a big-endian IEEE754 double-precision floating-point (8 bytes). + #[inline] fn write_be_f64(&mut self, f: f64) -> IoResult<()> { unsafe { self.write_be_u64(transmute(f)) @@ -1174,6 +1190,7 @@ fn write_be_f64(&mut self, f: f64) -> IoResult<()> { } /// Write a big-endian IEEE754 single-precision floating-point (4 bytes). + #[inline] fn write_be_f32(&mut self, f: f32) -> IoResult<()> { unsafe { self.write_be_u32(transmute(f)) @@ -1181,37 +1198,44 @@ fn write_be_f32(&mut self, f: f32) -> IoResult<()> { } /// Write a little-endian u64 (8 bytes). + #[inline] fn write_le_u64(&mut self, n: u64) -> IoResult<()> { extensions::u64_to_le_bytes(n, 8u, |v| self.write(v)) } /// Write a little-endian u32 (4 bytes). + #[inline] fn write_le_u32(&mut self, n: u32) -> IoResult<()> { extensions::u64_to_le_bytes(n as u64, 4u, |v| self.write(v)) } /// Write a little-endian u16 (2 bytes). + #[inline] fn write_le_u16(&mut self, n: u16) -> IoResult<()> { extensions::u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) } /// Write a little-endian i64 (8 bytes). + #[inline] fn write_le_i64(&mut self, n: i64) -> IoResult<()> { extensions::u64_to_le_bytes(n as u64, 8u, |v| self.write(v)) } /// Write a little-endian i32 (4 bytes). + #[inline] fn write_le_i32(&mut self, n: i32) -> IoResult<()> { extensions::u64_to_le_bytes(n as u64, 4u, |v| self.write(v)) } /// Write a little-endian i16 (2 bytes). + #[inline] fn write_le_i16(&mut self, n: i16) -> IoResult<()> { extensions::u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) } /// Write a little-endian IEEE754 double-precision floating-point /// (8 bytes). + #[inline] fn write_le_f64(&mut self, f: f64) -> IoResult<()> { unsafe { self.write_le_u64(transmute(f)) @@ -1220,6 +1244,7 @@ fn write_le_f64(&mut self, f: f64) -> IoResult<()> { /// Write a little-endian IEEE754 single-precision floating-point /// (4 bytes). + #[inline] fn write_le_f32(&mut self, f: f32) -> IoResult<()> { unsafe { self.write_le_u32(transmute(f)) @@ -1227,11 +1252,13 @@ fn write_le_f32(&mut self, f: f32) -> IoResult<()> { } /// Write a u8 (1 byte). + #[inline] fn write_u8(&mut self, n: u8) -> IoResult<()> { self.write([n]) } /// Write an i8 (1 byte). + #[inline] fn write_i8(&mut self, n: i8) -> IoResult<()> { self.write([n as u8]) } @@ -1240,18 +1267,25 @@ fn write_i8(&mut self, n: i8) -> IoResult<()> { /// /// This is useful to allow applying wrappers while still /// retaining ownership of the original value. + #[inline] fn by_ref<'a>(&'a mut self) -> RefWriter<'a, Self> { RefWriter { inner: self } } } impl Writer for Box { + #[inline] fn write(&mut self, buf: &[u8]) -> IoResult<()> { self.write(buf) } + + #[inline] fn flush(&mut self) -> IoResult<()> { self.flush() } } impl<'a> Writer for &'a mut Writer { + #[inline] fn write(&mut self, buf: &[u8]) -> IoResult<()> { self.write(buf) } + + #[inline] fn flush(&mut self) -> IoResult<()> { self.flush() } } @@ -1285,7 +1319,10 @@ pub struct RefWriter<'a, W> { } impl<'a, W: Writer> Writer for RefWriter<'a, W> { + #[inline] fn write(&mut self, buf: &[u8]) -> IoResult<()> { self.inner.write(buf) } + + #[inline] fn flush(&mut self) -> IoResult<()> { self.inner.flush() } } -- 2.44.0