Skip to content
Snippets Groups Projects
Commit e4447220 authored by kohensu's avatar kohensu Committed by Carl Lerche
Browse files

Improve performance of Buf::get_*() (#195)

The new implementation tries to get the data directly from bytes() (this is
possible most of the time) and if there is not enough data in bytes() use the
previous code: copy the needed bytes in a temporary buffer before returning
the data

Here the bench results:
                               Before                After           x-faster
get_f32::cursor             64 ns/iter (+/- 0)    20 ns/iter (+/- 0)    3.2
get_f32::tbuf_1             77 ns/iter (+/- 1)    34 ns/iter (+/- 0)    2.3
get_f32::tbuf_1_costly      87 ns/iter (+/- 0)    62 ns/iter (+/- 0)    1.4
get_f32::tbuf_2            151 ns/iter (+/- 18)  160 ns/iter (+/- 1)    0.9
get_f32::tbuf_2_costly     180 ns/iter (+/- 2)   187 ns/iter (+/- 2)    1.0

get_f64::cursor             67 ns/iter (+/- 0)    21 ns/iter (+/- 0)    3.2
get_f64::tbuf_1             80 ns/iter (+/- 0)    35 ns/iter (+/- 0)    2.3
get_f64::tbuf_1_costly      82 ns/iter (+/- 3)    60 ns/iter (+/- 0)    1.4
get_f64::tbuf_2            154 ns/iter (+/- 1)   164 ns/iter (+/- 0)    0.9
get_f64::tbuf_2_costly     170 ns/iter (+/- 2)   187 ns/iter (+/- 1)    0.9

get_u16::cursor             66 ns/iter (+/- 0)    20 ns/iter (+/- 0)    3.3
get_u16::tbuf_1             77 ns/iter (+/- 0)    35 ns/iter (+/- 0)    2.2
get_u16::tbuf_1_costly      85 ns/iter (+/- 2)    62 ns/iter (+/- 0)    1.4
get_u16::tbuf_2            147 ns/iter (+/- 0)   154 ns/iter (+/- 0)    1.0
get_u16::tbuf_2_costly     160 ns/iter (+/- 1)   177 ns/iter (+/- 0)    0.9

get_u32::cursor             64 ns/iter (+/- 0)    20 ns/iter (+/- 0)    3.2
get_u32::tbuf_1             77 ns/iter (+/- 0)    35 ns/iter (+/- 0)    2.2
get_u32::tbuf_1_costly      91 ns/iter (+/- 2)    63 ns/iter (+/- 0)    1.4
get_u32::tbuf_2            151 ns/iter (+/- 40)  157 ns/iter (+/- 0)    1.0
get_u32::tbuf_2_costly     162 ns/iter (+/- 0)   180 ns/iter (+/- 0)    0.9

get_u64::cursor             67 ns/iter (+/- 0)    20 ns/iter (+/- 0)    3.4
get_u64::tbuf_1             78 ns/iter (+/- 0)    35 ns/iter (+/- 1)    2.2
get_u64::tbuf_1_costly      87 ns/iter (+/- 1)    59 ns/iter (+/- 1)    1.5
get_u64::tbuf_2            154 ns/iter (+/- 0)   160 ns/iter (+/- 0)    1.0
get_u64::tbuf_2_costly     168 ns/iter (+/- 0)   184 ns/iter (+/- 0)    0.9

get_u8::cursor              64 ns/iter (+/- 0)    19 ns/iter (+/- 0)    3.4
get_u8::tbuf_1              77 ns/iter (+/- 0)    35 ns/iter (+/- 0)    2.2
get_u8::tbuf_1_costly       68 ns/iter (+/- 0)    51 ns/iter (+/- 0)    1.3
get_u8::tbuf_2              85 ns/iter (+/- 0)    43 ns/iter (+/- 0)    2.0
get_u8::tbuf_2_costly       75 ns/iter (+/- 0)    61 ns/iter (+/- 0)    1.2
get_u8::option              77 ns/iter (+/- 0)    59 ns/iter (+/- 0)    1.3

Improvement on the basic std::Cursor implementation are clearly visible.

Other implementations are specific to the bench tests and just map a static
slice. Different variant are:
 - tbuf_1: only one call of 'bytes()' is needed.
 - tbuf_2: two calls of 'bytes()' is needed to read more than one byte.
 - _costly version are implemented with #[inline(never)] on 'bytes()',
   'remaining()' and 'advance()'.

The cases that are slower (slightly) correspond to implementations that are not
really realistic: more than one byte is never possible in one time
parent d5610062
No related branches found
No related tags found
No related merge requests found
......@@ -4,6 +4,38 @@ use iovec::IoVec;
use std::{cmp, io, ptr};
macro_rules! buf_get_impl {
($this:ident, $size:expr, $conv:path) => ({
// try to convert directly from the bytes
let ret = {
// this Option<ret> trick is to avoid keeping a borrow on self
// when advance() is called (mut borrow) and to call bytes() only once
if let Some(src) = $this.bytes().get(..($size)) {
Some($conv(src))
} else {
None
}
};
if let Some(ret) = ret {
// if the direct convertion was possible, advance and return
$this.advance($size);
return ret;
} else {
// if not we copy the bytes in a temp buffer then convert
let mut buf = [0; ($size)];
$this.copy_to_slice(&mut buf); // (do the advance)
return $conv(&buf);
}
});
($this:ident, $buf_size:expr, $conv:path, $len_to_read:expr) => ({
// The same trick as above does not improve the best case speed.
// It seems to be linked to the way the method is optimised by the compiler
let mut buf = [0; ($buf_size)];
$this.copy_to_slice(&mut buf[..($len_to_read)]);
return $conv(&buf[..($len_to_read)], $len_to_read);
});
}
/// Read bytes from a buffer.
///
/// A buffer stores bytes in memory such that read operations are infallible.
......@@ -243,9 +275,10 @@ pub trait Buf {
///
/// This function panics if there is no more remaining data in `self`.
fn get_u8(&mut self) -> u8 {
let mut buf = [0; 1];
self.copy_to_slice(&mut buf);
buf[0]
assert!(self.remaining() >= 1);
let ret = self.bytes()[0];
self.advance(1);
ret
}
/// Gets a signed 8 bit integer from `self`.
......@@ -266,9 +299,10 @@ pub trait Buf {
///
/// This function panics if there is no more remaining data in `self`.
fn get_i8(&mut self) -> i8 {
let mut buf = [0; 1];
self.copy_to_slice(&mut buf);
buf[0] as i8
assert!(self.remaining() >= 1);
let ret = self.bytes()[0] as i8;
self.advance(1);
ret
}
/// Gets an unsigned 16 bit integer from `self` in big-endian byte order.
......@@ -289,9 +323,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_u16(&mut self) -> u16 {
let mut buf = [0; 2];
self.copy_to_slice(&mut buf);
BigEndian::read_u16(&buf)
buf_get_impl!(self, 2, BigEndian::read_u16);
}
/// Gets an unsigned 16 bit integer from `self` in little-endian byte order.
......@@ -312,9 +344,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_u16_le(&mut self) -> u16 {
let mut buf = [0; 2];
self.copy_to_slice(&mut buf);
LittleEndian::read_u16(&buf)
buf_get_impl!(self, 2, LittleEndian::read_u16);
}
/// Gets a signed 16 bit integer from `self` in big-endian byte order.
......@@ -335,9 +365,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_i16(&mut self) -> i16 {
let mut buf = [0; 2];
self.copy_to_slice(&mut buf);
BigEndian::read_i16(&buf)
buf_get_impl!(self, 2, BigEndian::read_i16);
}
/// Gets a signed 16 bit integer from `self` in little-endian byte order.
......@@ -358,9 +386,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_i16_le(&mut self) -> i16 {
let mut buf = [0; 2];
self.copy_to_slice(&mut buf);
LittleEndian::read_i16(&buf)
buf_get_impl!(self, 2, LittleEndian::read_i16);
}
/// Gets an unsigned 32 bit integer from `self` in the big-endian byte order.
......@@ -381,9 +407,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_u32(&mut self) -> u32 {
let mut buf = [0; 4];
self.copy_to_slice(&mut buf);
BigEndian::read_u32(&buf)
buf_get_impl!(self, 4, BigEndian::read_u32);
}
/// Gets an unsigned 32 bit integer from `self` in the little-endian byte order.
......@@ -404,9 +428,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_u32_le(&mut self) -> u32 {
let mut buf = [0; 4];
self.copy_to_slice(&mut buf);
LittleEndian::read_u32(&buf)
buf_get_impl!(self, 4, LittleEndian::read_u32);
}
/// Gets a signed 32 bit integer from `self` in big-endian byte order.
......@@ -427,9 +449,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_i32(&mut self) -> i32 {
let mut buf = [0; 4];
self.copy_to_slice(&mut buf);
BigEndian::read_i32(&buf)
buf_get_impl!(self, 4, BigEndian::read_i32);
}
/// Gets a signed 32 bit integer from `self` in little-endian byte order.
......@@ -450,9 +470,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_i32_le(&mut self) -> i32 {
let mut buf = [0; 4];
self.copy_to_slice(&mut buf);
LittleEndian::read_i32(&buf)
buf_get_impl!(self, 4, LittleEndian::read_i32);
}
/// Gets an unsigned 64 bit integer from `self` in big-endian byte order.
......@@ -473,9 +491,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_u64(&mut self) -> u64 {
let mut buf = [0; 8];
self.copy_to_slice(&mut buf);
BigEndian::read_u64(&buf)
buf_get_impl!(self, 8, BigEndian::read_u64);
}
/// Gets an unsigned 64 bit integer from `self` in little-endian byte order.
......@@ -496,9 +512,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_u64_le(&mut self) -> u64 {
let mut buf = [0; 8];
self.copy_to_slice(&mut buf);
LittleEndian::read_u64(&buf)
buf_get_impl!(self, 8, LittleEndian::read_u64);
}
/// Gets a signed 64 bit integer from `self` in big-endian byte order.
......@@ -519,9 +533,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_i64(&mut self) -> i64 {
let mut buf = [0; 8];
self.copy_to_slice(&mut buf);
BigEndian::read_i64(&buf)
buf_get_impl!(self, 8, BigEndian::read_i64);
}
/// Gets a signed 64 bit integer from `self` in little-endian byte order.
......@@ -542,9 +554,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_i64_le(&mut self) -> i64 {
let mut buf = [0; 8];
self.copy_to_slice(&mut buf);
LittleEndian::read_i64(&buf)
buf_get_impl!(self, 8, LittleEndian::read_i64);
}
/// Gets an unsigned n-byte integer from `self` in big-endian byte order.
......@@ -565,9 +575,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_uint(&mut self, nbytes: usize) -> u64 {
let mut buf = [0; 8];
self.copy_to_slice(&mut buf[..nbytes]);
BigEndian::read_uint(&buf[..nbytes], nbytes)
buf_get_impl!(self, 8, BigEndian::read_uint, nbytes);
}
/// Gets an unsigned n-byte integer from `self` in little-endian byte order.
......@@ -588,9 +596,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_uint_le(&mut self, nbytes: usize) -> u64 {
let mut buf = [0; 8];
self.copy_to_slice(&mut buf[..nbytes]);
LittleEndian::read_uint(&buf[..nbytes], nbytes)
buf_get_impl!(self, 8, LittleEndian::read_uint, nbytes);
}
/// Gets a signed n-byte integer from `self` in big-endian byte order.
......@@ -611,9 +617,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_int(&mut self, nbytes: usize) -> i64 {
let mut buf = [0; 8];
self.copy_to_slice(&mut buf[..nbytes]);
BigEndian::read_int(&buf[..nbytes], nbytes)
buf_get_impl!(self, 8, BigEndian::read_int, nbytes);
}
/// Gets a signed n-byte integer from `self` in little-endian byte order.
......@@ -634,9 +638,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_int_le(&mut self, nbytes: usize) -> i64 {
let mut buf = [0; 8];
self.copy_to_slice(&mut buf[..nbytes]);
LittleEndian::read_int(&buf[..nbytes], nbytes)
buf_get_impl!(self, 8, LittleEndian::read_int, nbytes);
}
/// Gets an IEEE754 single-precision (4 bytes) floating point number from
......@@ -658,9 +660,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_f32(&mut self) -> f32 {
let mut buf = [0; 4];
self.copy_to_slice(&mut buf);
BigEndian::read_f32(&buf)
buf_get_impl!(self, 4, BigEndian::read_f32);
}
/// Gets an IEEE754 single-precision (4 bytes) floating point number from
......@@ -682,9 +682,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_f32_le(&mut self) -> f32 {
let mut buf = [0; 4];
self.copy_to_slice(&mut buf);
LittleEndian::read_f32(&buf)
buf_get_impl!(self, 4, LittleEndian::read_f32);
}
/// Gets an IEEE754 double-precision (8 bytes) floating point number from
......@@ -706,9 +704,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_f64(&mut self) -> f64 {
let mut buf = [0; 8];
self.copy_to_slice(&mut buf);
BigEndian::read_f64(&buf)
buf_get_impl!(self, 8, BigEndian::read_f64);
}
/// Gets an IEEE754 double-precision (8 bytes) floating point number from
......@@ -730,9 +726,7 @@ pub trait Buf {
///
/// This function panics if there is not enough remaining data in `self`.
fn get_f64_le(&mut self) -> f64 {
let mut buf = [0; 8];
self.copy_to_slice(&mut buf);
LittleEndian::read_f64(&buf)
buf_get_impl!(self, 8, LittleEndian::read_f64);
}
/// Transforms a `Buf` into a concrete buffer.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment