Skip to content

Commit 43ac8e5

Browse files
authored
Refactor Bytes to use an internal vtable (#298)
Bytes is a useful tool for managing multiple slices into the same region of memory, and the other things it used to have been removed to reduce complexity. The exact strategy for managing the multiple references is no longer hard-coded, but instead backing by a customizable vtable. - Removed ability to mutate the underlying memory from the `Bytes` type. - Removed the "inline" (SBO) mechanism in `Bytes`. The reduces a large amount of complexity, and improves performance when accessing the slice of bytes, since a branch is no longer needed to check if the data is inline. - Removed `Bytes` knowledge of `BytesMut` (`BytesMut` may grow that knowledge back at a future point.)
1 parent ebe9602 commit 43ac8e5

17 files changed

+2176
-2829
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@ serde = { version = "1.0", optional = true }
2828
either = { version = "1.5", default-features = false, optional = true }
2929

3030
[dev-dependencies]
31+
loom = "0.2.10"
3132
serde_test = "1.0"

benches/bytes.rs

Lines changed: 20 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -4,47 +4,11 @@
44
extern crate test;
55

66
use test::Bencher;
7-
use bytes::{Bytes, BytesMut, BufMut};
8-
9-
#[bench]
10-
fn alloc_small(b: &mut Bencher) {
11-
b.iter(|| {
12-
for _ in 0..1024 {
13-
test::black_box(BytesMut::with_capacity(12));
14-
}
15-
})
16-
}
17-
18-
#[bench]
19-
fn alloc_mid(b: &mut Bencher) {
20-
b.iter(|| {
21-
test::black_box(BytesMut::with_capacity(128));
22-
})
23-
}
24-
25-
#[bench]
26-
fn alloc_big(b: &mut Bencher) {
27-
b.iter(|| {
28-
test::black_box(BytesMut::with_capacity(4096));
29-
})
30-
}
31-
32-
#[bench]
33-
fn split_off_and_drop(b: &mut Bencher) {
34-
b.iter(|| {
35-
for _ in 0..1024 {
36-
let v = vec![10; 200];
37-
let mut b = Bytes::from(v);
38-
test::black_box(b.split_off(100));
39-
test::black_box(b);
40-
}
41-
})
42-
}
7+
use bytes::Bytes;
438

449
#[bench]
4510
fn deref_unique(b: &mut Bencher) {
46-
let mut buf = BytesMut::with_capacity(4096);
47-
buf.put(&[0u8; 1024][..]);
11+
let buf = Bytes::from(vec![0; 1024]);
4812

4913
b.iter(|| {
5014
for _ in 0..1024 {
@@ -53,30 +17,10 @@ fn deref_unique(b: &mut Bencher) {
5317
})
5418
}
5519

56-
#[bench]
57-
fn deref_unique_unroll(b: &mut Bencher) {
58-
let mut buf = BytesMut::with_capacity(4096);
59-
buf.put(&[0u8; 1024][..]);
60-
61-
b.iter(|| {
62-
for _ in 0..128 {
63-
test::black_box(&buf[..]);
64-
test::black_box(&buf[..]);
65-
test::black_box(&buf[..]);
66-
test::black_box(&buf[..]);
67-
test::black_box(&buf[..]);
68-
test::black_box(&buf[..]);
69-
test::black_box(&buf[..]);
70-
test::black_box(&buf[..]);
71-
}
72-
})
73-
}
74-
7520
#[bench]
7621
fn deref_shared(b: &mut Bencher) {
77-
let mut buf = BytesMut::with_capacity(4096);
78-
buf.put(&[0u8; 1024][..]);
79-
let _b2 = buf.split_off(1024);
22+
let buf = Bytes::from(vec![0; 1024]);
23+
let _b2 = buf.clone();
8024

8125
b.iter(|| {
8226
for _ in 0..1024 {
@@ -86,9 +30,8 @@ fn deref_shared(b: &mut Bencher) {
8630
}
8731

8832
#[bench]
89-
fn deref_inline(b: &mut Bencher) {
90-
let mut buf = BytesMut::with_capacity(8);
91-
buf.put(&[0u8; 8][..]);
33+
fn deref_static(b: &mut Bencher) {
34+
let buf = Bytes::from_static(b"hello world");
9235

9336
b.iter(|| {
9437
for _ in 0..1024 {
@@ -97,33 +40,6 @@ fn deref_inline(b: &mut Bencher) {
9740
})
9841
}
9942

100-
#[bench]
101-
fn deref_two(b: &mut Bencher) {
102-
let mut buf1 = BytesMut::with_capacity(8);
103-
buf1.put(&[0u8; 8][..]);
104-
105-
let mut buf2 = BytesMut::with_capacity(4096);
106-
buf2.put(&[0u8; 1024][..]);
107-
108-
b.iter(|| {
109-
for _ in 0..512 {
110-
test::black_box(&buf1[..]);
111-
test::black_box(&buf2[..]);
112-
}
113-
})
114-
}
115-
116-
#[bench]
117-
fn clone_inline(b: &mut Bencher) {
118-
let bytes = Bytes::from_static(b"hello world");
119-
120-
b.iter(|| {
121-
for _ in 0..1024 {
122-
test::black_box(&bytes.clone());
123-
}
124-
})
125-
}
126-
12743
#[bench]
12844
fn clone_static(b: &mut Bencher) {
12945
let bytes = Bytes::from_static("hello world 1234567890 and have a good byte 0987654321".as_bytes());
@@ -136,8 +52,8 @@ fn clone_static(b: &mut Bencher) {
13652
}
13753

13854
#[bench]
139-
fn clone_arc(b: &mut Bencher) {
140-
let bytes = Bytes::from("hello world 1234567890 and have a good byte 0987654321".as_bytes());
55+
fn clone_shared(b: &mut Bencher) {
56+
let bytes = Bytes::from(b"hello world 1234567890 and have a good byte 0987654321".to_vec());
14157

14258
b.iter(|| {
14359
for _ in 0..1024 {
@@ -147,42 +63,14 @@ fn clone_arc(b: &mut Bencher) {
14763
}
14864

14965
#[bench]
150-
fn alloc_write_split_to_mid(b: &mut Bencher) {
151-
b.iter(|| {
152-
let mut buf = BytesMut::with_capacity(128);
153-
buf.put_slice(&[0u8; 64]);
154-
test::black_box(buf.split_to(64));
155-
})
156-
}
157-
158-
#[bench]
159-
fn drain_write_drain(b: &mut Bencher) {
160-
let data = [0u8; 128];
66+
fn clone_arc_vec(b: &mut Bencher) {
67+
use std::sync::Arc;
68+
let bytes = Arc::new(b"hello world 1234567890 and have a good byte 0987654321".to_vec());
16169

16270
b.iter(|| {
163-
let mut buf = BytesMut::with_capacity(1024);
164-
let mut parts = Vec::with_capacity(8);
165-
166-
for _ in 0..8 {
167-
buf.put(&data[..]);
168-
parts.push(buf.split_to(128));
71+
for _ in 0..1024 {
72+
test::black_box(&bytes.clone());
16973
}
170-
171-
test::black_box(parts);
172-
})
173-
}
174-
175-
#[bench]
176-
fn fmt_write(b: &mut Bencher) {
177-
use std::fmt::Write;
178-
let mut buf = BytesMut::with_capacity(128);
179-
let s = "foo bar baz quux lorem ipsum dolor et";
180-
181-
b.bytes = s.len() as u64;
182-
b.iter(|| {
183-
let _ = write!(buf, "{}", s);
184-
test::black_box(&buf);
185-
unsafe { buf.set_len(0); }
18674
})
18775
}
18876

@@ -191,7 +79,7 @@ fn from_long_slice(b: &mut Bencher) {
19179
let data = [0u8; 128];
19280
b.bytes = data.len() as u64;
19381
b.iter(|| {
194-
let buf = BytesMut::from(&data[..]);
82+
let buf = Bytes::copy_from_slice(&data[..]);
19583
test::black_box(buf);
19684
})
19785
}
@@ -217,34 +105,14 @@ fn slice_short_from_arc(b: &mut Bencher) {
217105
})
218106
}
219107

220-
// Keep in sync with bytes.rs
221-
#[cfg(target_pointer_width = "64")]
222-
const INLINE_CAP: usize = 4 * 8 - 1;
223-
#[cfg(target_pointer_width = "32")]
224-
const INLINE_CAP: usize = 4 * 4 - 1;
225-
226-
#[bench]
227-
fn slice_avg_le_inline_from_arc(b: &mut Bencher) {
228-
b.iter(|| {
229-
// `clone` is to convert to ARC
230-
let b = Bytes::from(vec![17; 1024]).clone();
231-
for i in 0..1000 {
232-
// [1, INLINE_CAP]
233-
let len = 1 + i % (INLINE_CAP - 1);
234-
test::black_box(b.slice(i % 10..i % 10 + len));
235-
}
236-
})
237-
}
238-
239108
#[bench]
240-
fn slice_large_le_inline_from_arc(b: &mut Bencher) {
109+
fn split_off_and_drop(b: &mut Bencher) {
241110
b.iter(|| {
242-
// `clone` is to convert to ARC
243-
let b = Bytes::from(vec![17; 1024]).clone();
244-
for i in 0..1000 {
245-
// [INLINE_CAP - 10, INLINE_CAP]
246-
let len = INLINE_CAP - 9 + i % 10;
247-
test::black_box(b.slice(i % 10..i % 10 + len));
111+
for _ in 0..1024 {
112+
let v = vec![10; 200];
113+
let mut b = Bytes::from(v);
114+
test::black_box(b.split_off(100));
115+
test::black_box(b);
248116
}
249117
})
250118
}

benches/bytes_mut.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#![feature(test)]
2+
#![deny(warnings, rust_2018_idioms)]
3+
4+
extern crate test;
5+
6+
use test::Bencher;
7+
use bytes::{BufMut, BytesMut};
8+
9+
#[bench]
10+
fn alloc_small(b: &mut Bencher) {
11+
b.iter(|| {
12+
for _ in 0..1024 {
13+
test::black_box(BytesMut::with_capacity(12));
14+
}
15+
})
16+
}
17+
18+
#[bench]
19+
fn alloc_mid(b: &mut Bencher) {
20+
b.iter(|| {
21+
test::black_box(BytesMut::with_capacity(128));
22+
})
23+
}
24+
25+
#[bench]
26+
fn alloc_big(b: &mut Bencher) {
27+
b.iter(|| {
28+
test::black_box(BytesMut::with_capacity(4096));
29+
})
30+
}
31+
32+
33+
#[bench]
34+
fn deref_unique(b: &mut Bencher) {
35+
let mut buf = BytesMut::with_capacity(4096);
36+
buf.put(&[0u8; 1024][..]);
37+
38+
b.iter(|| {
39+
for _ in 0..1024 {
40+
test::black_box(&buf[..]);
41+
}
42+
})
43+
}
44+
45+
#[bench]
46+
fn deref_unique_unroll(b: &mut Bencher) {
47+
let mut buf = BytesMut::with_capacity(4096);
48+
buf.put(&[0u8; 1024][..]);
49+
50+
b.iter(|| {
51+
for _ in 0..128 {
52+
test::black_box(&buf[..]);
53+
test::black_box(&buf[..]);
54+
test::black_box(&buf[..]);
55+
test::black_box(&buf[..]);
56+
test::black_box(&buf[..]);
57+
test::black_box(&buf[..]);
58+
test::black_box(&buf[..]);
59+
test::black_box(&buf[..]);
60+
}
61+
})
62+
}
63+
64+
#[bench]
65+
fn deref_shared(b: &mut Bencher) {
66+
let mut buf = BytesMut::with_capacity(4096);
67+
buf.put(&[0u8; 1024][..]);
68+
let _b2 = buf.split_off(1024);
69+
70+
b.iter(|| {
71+
for _ in 0..1024 {
72+
test::black_box(&buf[..]);
73+
}
74+
})
75+
}
76+
77+
#[bench]
78+
fn deref_two(b: &mut Bencher) {
79+
let mut buf1 = BytesMut::with_capacity(8);
80+
buf1.put(&[0u8; 8][..]);
81+
82+
let mut buf2 = BytesMut::with_capacity(4096);
83+
buf2.put(&[0u8; 1024][..]);
84+
85+
b.iter(|| {
86+
for _ in 0..512 {
87+
test::black_box(&buf1[..]);
88+
test::black_box(&buf2[..]);
89+
}
90+
})
91+
}
92+
93+
#[bench]
94+
fn clone_frozen(b: &mut Bencher) {
95+
let bytes = BytesMut::from(&b"hello world 1234567890 and have a good byte 0987654321"[..]).split().freeze();
96+
97+
b.iter(|| {
98+
for _ in 0..1024 {
99+
test::black_box(&bytes.clone());
100+
}
101+
})
102+
}
103+
104+
#[bench]
105+
fn alloc_write_split_to_mid(b: &mut Bencher) {
106+
b.iter(|| {
107+
let mut buf = BytesMut::with_capacity(128);
108+
buf.put_slice(&[0u8; 64]);
109+
test::black_box(buf.split_to(64));
110+
})
111+
}
112+
113+
#[bench]
114+
fn drain_write_drain(b: &mut Bencher) {
115+
let data = [0u8; 128];
116+
117+
b.iter(|| {
118+
let mut buf = BytesMut::with_capacity(1024);
119+
let mut parts = Vec::with_capacity(8);
120+
121+
for _ in 0..8 {
122+
buf.put(&data[..]);
123+
parts.push(buf.split_to(128));
124+
}
125+
126+
test::black_box(parts);
127+
})
128+
}
129+
130+
#[bench]
131+
fn fmt_write(b: &mut Bencher) {
132+
use std::fmt::Write;
133+
let mut buf = BytesMut::with_capacity(128);
134+
let s = "foo bar baz quux lorem ipsum dolor et";
135+
136+
b.bytes = s.len() as u64;
137+
b.iter(|| {
138+
let _ = write!(buf, "{}", s);
139+
test::black_box(&buf);
140+
unsafe { buf.set_len(0); }
141+
})
142+
}

ci/azure-cross-compile.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
parameters:
2-
cmd: test
2+
cmd: build
33
rust_version: stable
44

55
jobs:

0 commit comments

Comments
 (0)