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

Fallback when allocating a new chunk fails #111

Merged
merged 7 commits into from
May 26, 2021

Conversation

Kerollmops
Copy link
Contributor

@Kerollmops Kerollmops commented May 12, 2021

When the global allocator is unable to allocate a new chunk, mostly due to the size, the Bump struct was either panicking or returning an AllocErr. This PR, discussed in a comment, changes the policy of the Bump::alloc_layout_slow:

  1. Try to allocate a chunk twice the size of the last one,
  2. If rejected, try with a chunk of the same size as the last one,
  3. If rejected, try with a chunk of half the size of the last one,
  4. If rejected, continue trying with a size half the previously tried one,
  5. If the size tried is smaller than the default chunk size, return None instead of the footer, the same behavior as before.

Trying to allocate half the size of the last chunk helps in allocating the whole available memory, it creates smaller allocations that will more easily fit in the available global memory.

when twice the current footer size cannot be allocated.

This is done by modifying the Bump new_chunk parameters,
it now asks for the chunk size instead of computing it
on its size.
src/lib.rs Outdated
Comment on lines 684 to 685
debug_assert!(size >= old_size_with_footer.unwrap_or(0) * 2);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about this change, what do you think? Do I need to move it to the alloc_layout_slow method?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you instead debug_assert! the property that the new chunk can hold the requested_layout if any?

@Kerollmops
Copy link
Contributor Author

Kerollmops commented May 12, 2021

I tried another allocating policy, where it always try to allocate half the previously tried allocated size, starting from twice the size of the last allocated chunk and stopping when the size is smaller than the default chunk size. I implemented this because I again faced an allocation error due to the fact that the remaining memory what a little bit less than half the size of the last allocated chunk size (the current policy of this PR).

Copy link
Owner

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't have this behavior in new_chunk directly because that will break with_capacity's and try_with_capacity's guarantees that if it successfully returns then you have that much given capacity.

I think the way to go is by defining a new function that wraps new_chunk in a loop and tries to allocate double the current chunk, then the current chunk, then half the current chunk, then a quarter, etc... and fails if allocating the default chunk size fails or if it can't allocate something large enough for the requested allocation's layout.

Does that make sense?

@Kerollmops
Copy link
Contributor Author

It totally makes sense and this is why I didn't modify the new_chunk method a lot, I only changed the first parameter to be the expected chunk size instead of the old chunk size, this way it is easier to know what the new_chunk method will allocate. It doesn't impact the try_/with_capacity methods as they do not use this first parameter.

However, I modified the alloc_layout_slow method to implement the algorithm that you described in your comment.

Copy link
Owner

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Kerollmops! This looks good to me, modulo the little suggestions below. After those are addressed, I'll be happy to merge this! Thanks again!

src/lib.rs Outdated
Comment on lines 684 to 685
debug_assert!(size >= old_size_with_footer.unwrap_or(0) * 2);

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you instead debug_assert! the property that the new chunk can hold the requested_layout if any?

src/lib.rs Outdated
// smaller than the default footer size.
let mut base_size = (current_layout.size() - FOOTER_SIZE).checked_mul(2)?;
let sizes = iter::from_fn(|| {
if base_size >= DEFAULT_CHUNK_SIZE_WITHOUT_FOOTER {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if base_size >= DEFAULT_CHUNK_SIZE_WITHOUT_FOOTER {
if base_size >= DEFAULT_CHUNK_SIZE_WITHOUT_FOOTER && base_size >= layout.size {

This isn't technically 100% necessary for correctness, since Bump::new_chunk will round up to the requested layout's size/align. However, consider the scenario where the requested size is very large and the global allocator can't fulfill it: if we didn't have this additional check, we would ask for smaller and smaller chunks, but new_chunk would have to round those chunk sizes up to the exact same requested layout size every time, effectively making a pretty-much-known-to-fail malloc call log n times.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also combine the checks with something like this:

let min_new_chunk_size = layout.size.max(DEFAULT_CHUNK_SIZE_WITHOUT_FOOTER);
let sizes = iter::from_fn(|| {
    if base_size >= min_new_chunk_size {
        // ...
    } else {
        // ...
    }
});

src/lib.rs Outdated
Comment on lines 1445 to 1446
// As a sane default, we want our new allocation to be about twice as
// big as the previous allocation. If the global allocator refuses it,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Teensy tiny nitpick over wording.

Suggested change
// As a sane default, we want our new allocation to be about twice as
// big as the previous allocation. If the global allocator refuses it,
// By default, we want our new chunk to be about twice as
// big as the previous chunk. If the global allocator refuses it,

@Kerollmops Kerollmops requested a review from fitzgen May 25, 2021 09:05
Copy link
Owner

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Thanks for your patience with the multiple rounds of review.

@fitzgen
Copy link
Owner

fitzgen commented May 25, 2021

Looks like there are errors in CI:

https://github.com/fitzgen/bumpalo/pull/111/checks?check_run_id=2667680337#step:6:246

Can you reproduce this locally?

@Kerollmops
Copy link
Contributor Author

Hey, yes, I just fixed it, what do you think?

Looks great! Thanks for your patience with the multiple rounds of review.

No worries, it is completely understandable 😃

Copy link
Owner

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!!

@fitzgen fitzgen merged commit 4c972de into fitzgen:master May 26, 2021
@Kerollmops Kerollmops deleted the fallback-alloc-new-chunk branch May 26, 2021 19:08
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

Successfully merging this pull request may close these issues.

None yet

2 participants