mirror of
https://github.com/tokio-rs/bytes.git
synced 2026-08-07 00:00:13 +02:00
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
This commit is contained in:
+60
-66
@@ -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
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@@ -297,9 +331,7 @@ pub trait Buf {
|
||||
///
|
||||
/// This function panics if there is not enough remaining data in `self`.
|
||||
fn get_u16_be(&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.
|
||||
@@ -320,9 +352,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);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@@ -351,9 +381,7 @@ pub trait Buf {
|
||||
///
|
||||
/// This function panics if there is not enough remaining data in `self`.
|
||||
fn get_i16_be(&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.
|
||||
@@ -374,9 +402,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);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@@ -405,9 +431,7 @@ pub trait Buf {
|
||||
///
|
||||
/// This function panics if there is not enough remaining data in `self`.
|
||||
fn get_u32_be(&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.
|
||||
@@ -428,9 +452,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);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@@ -459,9 +481,7 @@ pub trait Buf {
|
||||
///
|
||||
/// This function panics if there is not enough remaining data in `self`.
|
||||
fn get_i32_be(&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.
|
||||
@@ -482,9 +502,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);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@@ -513,9 +531,7 @@ pub trait Buf {
|
||||
///
|
||||
/// This function panics if there is not enough remaining data in `self`.
|
||||
fn get_u64_be(&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.
|
||||
@@ -536,9 +552,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);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@@ -567,9 +581,7 @@ pub trait Buf {
|
||||
///
|
||||
/// This function panics if there is not enough remaining data in `self`.
|
||||
fn get_i64_be(&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.
|
||||
@@ -590,9 +602,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);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@@ -621,9 +631,7 @@ pub trait Buf {
|
||||
///
|
||||
/// This function panics if there is not enough remaining data in `self`.
|
||||
fn get_uint_be(&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.
|
||||
@@ -644,9 +652,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);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@@ -675,9 +681,7 @@ pub trait Buf {
|
||||
///
|
||||
/// This function panics if there is not enough remaining data in `self`.
|
||||
fn get_int_be(&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.
|
||||
@@ -698,9 +702,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);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@@ -730,9 +732,7 @@ pub trait Buf {
|
||||
///
|
||||
/// This function panics if there is not enough remaining data in `self`.
|
||||
fn get_f32_be(&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
|
||||
@@ -754,9 +754,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);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@@ -786,9 +784,7 @@ pub trait Buf {
|
||||
///
|
||||
/// This function panics if there is not enough remaining data in `self`.
|
||||
fn get_f64_be(&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
|
||||
@@ -810,9 +806,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.
|
||||
|
||||
Reference in New Issue
Block a user