Optimize Bytes::slice for short slices (#136)

Slice operation should return inline when possible

It is cheaper than atomic increment/decrement.

Before this patch:

```
test slice_avg_le_inline_from_arc   ... bench:      28,582 ns/iter (+/- 3,880)
test slice_empty                    ... bench:       8,797 ns/iter (+/- 1,325)
test slice_large_le_inline_from_arc ... bench:      27,684 ns/iter (+/- 5,920)
test slice_short_from_arc           ... bench:      27,439 ns/iter (+/- 5,783)
```

After this patch:

```
test slice_avg_le_inline_from_arc   ... bench:      18,872 ns/iter (+/- 2,937)
test slice_empty                    ... bench:       9,136 ns/iter (+/- 1,908)
test slice_large_le_inline_from_arc ... bench:      18,052 ns/iter (+/- 2,981)
test slice_short_from_arc           ... bench:      18,200 ns/iter (+/- 2,534)
```
This commit is contained in:
Stepan Koltsov
2017-07-01 14:10:09 -07:00
committed by Carl Lerche
parent 544997958f
commit b9ccd2a866
2 changed files with 49 additions and 12 deletions
+41 -8
View File
@@ -154,19 +154,52 @@ fn from_long_slice(b: &mut Bencher) {
#[bench] #[bench]
fn slice_empty(b: &mut Bencher) { fn slice_empty(b: &mut Bencher) {
b.iter(|| { b.iter(|| {
// Use empty vec to avoid measure of allocation/deallocation let b = Bytes::from(vec![17; 1024]).clone();
let bytes = Bytes::from(Vec::new()); for i in 0..1000 {
(bytes.slice(0, 0), bytes) test::black_box(b.slice(i % 100, i % 100));
}
}) })
} }
#[bench] #[bench]
fn slice_not_empty(b: &mut Bencher) { fn slice_short_from_arc(b: &mut Bencher) {
b.iter(|| { b.iter(|| {
let b = Bytes::from(b"aabbccddeeffgghh".to_vec()); // `clone` is to convert to ARC
for _ in 0..1024 { let b = Bytes::from(vec![17; 1024]).clone();
test::black_box(b.slice(3, 5)); for i in 0..1000 {
test::black_box(&b); test::black_box(b.slice(1, 2 + i % 10));
}
})
}
// Keep in sync with bytes.rs
#[cfg(target_pointer_width = "64")]
const INLINE_CAP: usize = 4 * 8 - 1;
#[cfg(target_pointer_width = "32")]
const INLINE_CAP: usize = 4 * 4 - 1;
#[bench]
fn slice_avg_le_inline_from_arc(b: &mut Bencher) {
b.iter(|| {
// `clone` is to convert to ARC
let b = Bytes::from(vec![17; 1024]).clone();
for i in 0..1000 {
// [1, INLINE_CAP]
let len = 1 + i % (INLINE_CAP - 1);
test::black_box(b.slice(i % 10, i % 10 + len));
}
})
}
#[bench]
fn slice_large_le_inline_from_arc(b: &mut Bencher) {
b.iter(|| {
// `clone` is to convert to ARC
let b = Bytes::from(vec![17; 1024]).clone();
for i in 0..1000 {
// [INLINE_CAP - 10, INLINE_CAP]
let len = INLINE_CAP - 9 + i % 10;
test::black_box(b.slice(i % 10, i % 10 + len));
} }
}) })
} }
+8 -4
View File
@@ -492,9 +492,11 @@ impl Bytes {
/// Requires that `begin <= end` and `end <= self.len()`, otherwise slicing /// Requires that `begin <= end` and `end <= self.len()`, otherwise slicing
/// will panic. /// will panic.
pub fn slice(&self, begin: usize, end: usize) -> Bytes { pub fn slice(&self, begin: usize, end: usize) -> Bytes {
if begin == end { assert!(begin <= end);
assert!(begin <= self.len()); assert!(end <= self.len());
return Bytes::new();
if end - begin <= INLINE_CAP {
return Bytes::from(&self[begin..end]);
} }
let mut ret = self.clone(); let mut ret = self.clone();
@@ -1430,7 +1432,9 @@ impl<'a> From<&'a [u8]> for BytesMut {
fn from(src: &'a [u8]) -> BytesMut { fn from(src: &'a [u8]) -> BytesMut {
let len = src.len(); let len = src.len();
if len <= INLINE_CAP { if len == 0 {
BytesMut::new()
} else if len <= INLINE_CAP {
unsafe { unsafe {
let mut inner: Inner = mem::uninitialized(); let mut inner: Inner = mem::uninitialized();