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-02 00:10:09 +03: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]
fn slice_empty(b: &mut Bencher) {
b.iter(|| {
// Use empty vec to avoid measure of allocation/deallocation
let bytes = Bytes::from(Vec::new());
(bytes.slice(0, 0), bytes)
let b = Bytes::from(vec![17; 1024]).clone();
for i in 0..1000 {
test::black_box(b.slice(i % 100, i % 100));
}
})
}
#[bench]
fn slice_not_empty(b: &mut Bencher) {
fn slice_short_from_arc(b: &mut Bencher) {
b.iter(|| {
let b = Bytes::from(b"aabbccddeeffgghh".to_vec());
for _ in 0..1024 {
test::black_box(b.slice(3, 5));
test::black_box(&b);
// `clone` is to convert to ARC
let b = Bytes::from(vec![17; 1024]).clone();
for i in 0..1000 {
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
/// will panic.
pub fn slice(&self, begin: usize, end: usize) -> Bytes {
if begin == end {
assert!(begin <= self.len());
return Bytes::new();
assert!(begin <= end);
assert!(end <= self.len());
if end - begin <= INLINE_CAP {
return Bytes::from(&self[begin..end]);
}
let mut ret = self.clone();
@@ -1430,7 +1432,9 @@ impl<'a> From<&'a [u8]> for BytesMut {
fn from(src: &'a [u8]) -> BytesMut {
let len = src.len();
if len <= INLINE_CAP {
if len == 0 {
BytesMut::new()
} else if len <= INLINE_CAP {
unsafe {
let mut inner: Inner = mem::uninitialized();