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

Capnp message growing unbounded when reusing Builder to save on allocation #523

Open
gtsui opened this issue Oct 7, 2024 · 1 comment
Open

Comments

@gtsui
Copy link

gtsui commented Oct 7, 2024

In my code below, I want to reuse my capnp message object because I dont want to have to allocate new memory on every cycle of my loop. However, when I run it this way, the size of my message object seems to grow and grow.

I'm sure there must be some way to have capnp write at the beginning of my message rather than appending to it, or clear it, but I'm not sure how? Am I missing something simple?

fn main() -> Result<(), anyhow::Error> {
    let mut message = capnp::message::Builder::new(HeapAllocator::new().first_segment_words(128));
    let mut buffer: Vec<u8> = Vec::with_capacity(1024);
    for i in 0..100 {
        buffer.clear();
        let mut builder = message.init_root::<book_capnp::book::Builder>();
        builder.set_author("Mark Twain");
        builder.set_title("Huckleberry Finn");
        builder.set_pages(400);
        capnp::serialize::write_message(&mut buffer, &message)?;
        println!("{:?}", buffer.len());
        println!("{:?}", message.size_in_words());
        println!("=====");
    }
    Ok(())
}

Output:

80
9
=====
144
17
=====
//...
7104
886
=====
@gtsui gtsui changed the title Capnp message growing unbounded Capnp message growing unbounded when reusing Builder to save on allocation Oct 7, 2024
@dwrensha
Copy link
Member

dwrensha commented Oct 7, 2024

You can reuse the first segment of your message builder via ScratchSpaceHeapAllocator:

/// An Allocator whose first segment is a backed by a user-provided buffer.
///
/// Recall that an `Allocator` implementation must ensure that allocated segments are
/// initially *zeroed*. `ScratchSpaceHeapAllocator` ensures that is the case by zeroing
/// the entire buffer upon initial construction, and then zeroing any *potentially used*
/// part of the buffer upon `deallocate_segment()`.
///
/// You can reuse a `ScratchSpaceHeapAllocator` by calling `message::Builder::into_allocator()`,
/// or by initially passing it to `message::Builder::new()` as a `&mut ScratchSpaceHeapAllocator`.
/// Such reuse can save significant amounts of zeroing.
#[cfg(feature = "alloc")]
pub struct ScratchSpaceHeapAllocator<'a> {
scratch_space: &'a mut [u8],
scratch_space_allocated: bool,
allocator: HeapAllocator,
}

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

2 participants