Skip to content

io::Write via buf::Writer on BytesMut significantly slower than on Vec<u8> #531

Open
@pablosichert

Description

@pablosichert

Heyo there,

when writing serde_json::to_writer on BytesMut vs. writing serde_json::to_vec I saw a slowdown of roughly 40% depending on input size – while I expected them to perform equally fast.

The following benchmarks

#[bench]
fn bytes_mut_io_write(b: &mut Bencher) {
    use std::io::Write;
    let mut buffer = BytesMut::with_capacity(128);
    let bytes = b"foo bar baz quux lorem ipsum dolor et";

    b.bytes = bytes.len() as u64;
    b.iter(|| {
        (&mut buffer).writer().write(bytes).unwrap();
        test::black_box(&buffer);
        unsafe {
            buffer.set_len(0);
        }
    })
}

#[bench]
fn vec_io_write(b: &mut Bencher) {
    use std::io::Write;
    let mut buffer = Vec::with_capacity(128);
    let bytes = b"foo bar baz quux lorem ipsum dolor et";

    b.bytes = bytes.len() as u64;
    b.iter(|| {
        buffer.write(bytes).unwrap();
        test::black_box(&buffer);
        unsafe {
            buffer.set_len(0);
        }
    })
}

yielded

bytes $ cargo +nightly bench --bench bytes_mut vec_io_write
    Finished bench [optimized] target(s) in 0.00s
     Running unittests (target/release/deps/bytes_mut-d70aabe3863f1aa3)

running 1 test
test vec_io_write             ... bench:           2 ns/iter (+/- 0) = 18500 MB/s

test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 19 filtered out; finished in 5.73s
bytes $ cargo +nightly bench --bench bytes_mut bytes_mut_io_write
    Finished bench [optimized] target(s) in 0.00s
     Running unittests (target/release/deps/bytes_mut-d70aabe3863f1aa3)

running 1 test
test bytes_mut_io_write       ... bench:           7 ns/iter (+/- 0) = 5285 MB/s

test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 19 filtered out; finished in 0.17s

Some more integrated benchmarks:

pub fn to_bytes<T>(value: &T) -> serde_json::Result<BytesMut>
where
    T: ?Sized + Serialize,
{
    let mut bytes = BytesMut::with_capacity(128);
    serde_json::to_writer((&mut bytes).writer(), value)?;
    Ok(bytes)
}
to_bytes(&log)

279.92 ns


pub fn to_bytes<T>(value: &T) -> serde_json::Result<BytesMut>
where
    T: ?Sized + Serialize,
{
    let bytes = serde_json::to_vec(value)?;
    let bytes = BytesMut::from(bytes.as_slice());
    Ok(bytes)
}
to_bytes(&log)

226.44 ns


serde_json::to_vec(&log)

195.28 ns

Give that even writing to Vec<u8> and copying the result over to a new BytesMut was faster than taking the Writer, I think this could use some attention.

I'm wondering if this is a conceputal difference in BytesMut or something that could be improved via #425/#478?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions