Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable shrinking of allocations #679

Open
tgnottingham opened this issue Mar 18, 2024 · 0 comments
Open

Enable shrinking of allocations #679

tgnottingham opened this issue Mar 18, 2024 · 0 comments

Comments

@tgnottingham
Copy link

tgnottingham commented Mar 18, 2024

I would like to be able to shrink allocations in certain cases, but I don't immediately see how to do that.

I should mention that my understanding of this crate is limited, so there's a strong possibility I'm using it wrong. And I also realize that whether or not an allocator would actually shrink an allocation without copying is up to the implementation.

Here's a semi-abstract example:

use bytes::BytesMut;

fn main() {
    const N: usize = 2 * 1024 * 1024;

    // b1 contains a lot of data, relatively. Let's say that it contains some large HTTP requests.
    let mut b1 = BytesMut::zeroed(N);
    print("b1", &b1);
    println!();

    // Prints:
    // b1 -- addr: 0x75af143ff010, len: 2097152, cap: 2097152, spare: 0

    // We parse b1, and find that we only need to split off a small part of it. E.g. we want to
    // split off an HTTP header value (ignoring that the code doesn't exactly illustrate this).
    let b2 = b1.split_to(32);
    print("b1", &b1);
    print("b2", &b2);
    println!();

    // Prints:
    // b1 -- addr: 0x75af143ff030, len: 2097120, cap: 2097120, spare: 0
    // b2 -- addr: 0x75af143ff010, len: 32, cap: 32, spare: 0

    // Later, some new data comes in, and we add more data to b1, causing it to reallocate. b1's
    // old capacity doesn't get deallocated, so b2 would be able to make use of it were we to add
    // to b2 (though b2 doesn't report it as part of its capacity). But if we don't have a need to
    // add to b2, that space is wasted. It seems the only way to deallocate it would be to copy b2
    // to another location, which would undermine our reason for using the bytes crate. :/
    b1.extend_from_slice(b"1");
    print("b1", &b1);
    print("b2", &b2);
    println!();

    // Prints:
    // b1 -- addr: 0x75af141ff010, len: 2097121, cap: 2097121, spare: 0
    // b2 -- addr: 0x75af143ff010, len: 32, cap: 32, spare: 0
}

fn print(name: &str, bm: &BytesMut) {
    let addr = bm.as_ptr();
    let cap = bm.capacity();
    let len = bm.len();
    let spare = cap - len;
    println!("{} -- addr: {:p}, len: {}, cap: {}, spare: {}", name, addr, len, cap, spare);
}

The issue is the same whether or not we freeze b2 into Bytes.

It would be nice if there were some way to shrink the b2 allocation, but I don't know what the best way of doing that would be.

If there were an explicit function along the lines of Vec::shrink_to_fit, the code using b2 wouldn't necessarily know when b1 had reallocated and therefore when a good time to call that shrink function would be. Still, this option might be acceptable, especially if the function was extremely inexpensive when shrinking wasn't necessary, so that the code using b2 could call it periodically without concern of too much of a performance hit.

And I imagine this would only work if the Bytes or BytesMut you're trying to shrink is the only one still referencing the allocation, which makes it less useful.

So, is there a good way of making shrinking allocations possible? And am I this crate wrong? :)

Edit 1: noticed that this issue is similar to #634, though it's not quite the same, as that's about adding documentation concerning deallocations, and this is more of a feature request for shrinking allocations.

Edit 2: Also saw a similar issue discussed in #401, which implies that shrinking would incur a copy in practice (at least given the default allocator?).

I'm guessing the advice in #634 (comment) is going to be the answer here, but I'd be happy to hear otherwise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant