mirror of
https://github.com/tokio-rs/bytes.git
synced 2026-08-14 00:00:13 +02:00
Avoid large reallocations when freezing BytesMut (#592)
When we freeze a BytesMut, we turn it into a Vec, and then convert that to a Bytes. Currently, this happen using Vec::into_boxed_slice, which reallocates to a slice of the same length as the Vev if the length and the capacity are not equal. This can pose a performance problem if the Vec is large or if this happens many times in a loop. Instead, let's compare the length and capacity, and if they're the same, continue to handle this using into_boxed_slice. Otherwise, since we have a type of vtable which can handle a separate capacity, the shared vtable, let's turn our Vec into that kind of Bytes. While this does not avoid allocation altogether, it performs a fixed size allocation and avoids any need to memcpy.
This commit is contained in:
+30
-2
@@ -807,8 +807,36 @@ impl From<&'static str> for Bytes {
|
||||
|
||||
impl From<Vec<u8>> for Bytes {
|
||||
fn from(vec: Vec<u8>) -> Bytes {
|
||||
let slice = vec.into_boxed_slice();
|
||||
slice.into()
|
||||
let mut vec = vec;
|
||||
let ptr = vec.as_mut_ptr();
|
||||
let len = vec.len();
|
||||
let cap = vec.capacity();
|
||||
|
||||
// Avoid an extra allocation if possible.
|
||||
if len == cap {
|
||||
return Bytes::from(vec.into_boxed_slice());
|
||||
}
|
||||
|
||||
let shared = Box::new(Shared {
|
||||
buf: ptr,
|
||||
cap,
|
||||
ref_cnt: AtomicUsize::new(1),
|
||||
});
|
||||
mem::forget(vec);
|
||||
|
||||
let shared = Box::into_raw(shared);
|
||||
// The pointer should be aligned, so this assert should
|
||||
// always succeed.
|
||||
debug_assert!(
|
||||
0 == (shared as usize & KIND_MASK),
|
||||
"internal: Box<Shared> should have an aligned pointer",
|
||||
);
|
||||
Bytes {
|
||||
ptr,
|
||||
len,
|
||||
data: AtomicPtr::new(shared as _),
|
||||
vtable: &SHARED_VTABLE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user