-
Notifications
You must be signed in to change notification settings - Fork 110
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
Add support for allocation limits on arenas #160
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! A few comments below.
ce26866
to
4e3a178
Compare
Hey @fitzgen, just checking in to see if you've had time to review the latest changes? No worries if you're busy though 😃 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Somehow this slipped through my email.
I have a few small comments below, but this generally looks good, and after those things are addressed we should be able to merge this.
Thanks for being patient!
f3fb2b0
to
8efa890
Compare
All the comments should be resolved with these commits 😄 |
On an unrelated note, between the time I opened the PR and now I started project selection for my 4th year project in school and am looking at doing work exploring the applications of WASM modules for IoT app development and I noticed that you're a maintainer of I'll hopefully be working with |
I just have one concern regarding how allocation limits interact with the value for the minimum chunk size allocation. If a limit is set that is below Should Useful documentation notes might have to effectively make the value of |
Ah this is a good point. What if we chain on the limit as a size option to let sizes = ...;
let sizes = sizes
.chain(match self.allocation_limit() {
Some(limit) if limit < DEFAULT_CHUNK_SIZE_WITHOUT_FOOTER
&& self.allocated_bytes() == 0 => Some(limit),
_ => None,
});
let new_footer = ...; And then the existing |
This mostly works but my testing finds it interacts badly with the page strategy for small allocations and the addition of memory for the footer, breaking the quickcheck for allocation limits never being exceeded. To make this work I think that either the size check needs to be moved back into I think this was technically a problem before the above change and was just masked by how the rounding + overhead + footer size adjustment is not significant for larger limits. This does have it's own problems though as if the check is just moved around the issue changes from happening when trying to allocate less than One way to work around this is to stipulate that the limit can be exceeded by some margin that covers the adjustment that |
@fitzgen Since I'm not sure if plain comments trigger notifications |
I took a stab at fixing this problem by factoring out the logic for determining the final size of chunk allocations so that the code in I also allowed the chunk size halving process to go below the min chunk size when dealing with small limits as just requesting a chunk with the limit value usually rounds up past the limit and ends up being rejected. |
I'm realizing now that this change causes some memory details about the new chunks to be calculated twice in a lot of cases so I'll make a change to avoid that now. |
I've made the change to cache the new chunk memory details so that they are not calculated twice but this necessitated a change in I'm not sure if that's an acceptable change and I'm not a fan of the name I have |
let chunk_memory_details = iter::from_fn(|| { | ||
let bypass_min_chunk_size_for_small_limits = match self.allocation_limit() { | ||
Some(limit) | ||
if layout.size() < limit | ||
&& base_size >= layout.size() | ||
&& limit < DEFAULT_CHUNK_SIZE_WITHOUT_FOOTER | ||
&& self.allocated_bytes() == 0 => | ||
{ | ||
true | ||
} | ||
_ => false, | ||
}; | ||
|
||
if base_size >= min_new_chunk_size || bypass_min_chunk_size_for_small_limits { | ||
let size = base_size; | ||
base_size = base_size / 2; | ||
Some(size) | ||
Bump::new_chunk_memory_details(Some(size), layout) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if rustc is smart enough to transform this into
if base_size >= min_new_chunk_size {
// ...
} else {
let bypass_min_chunk_size_for_small_limits = ...;
if bypass_min_chunk_size_for_small_limits {
let size = base_size;
base_size = base_size / 2;
Bump::new_chunk_memory_details(Some(size), layout)
} else {
None
}
}
but I decided to hold off on any premature optimizations since I think this is much less readable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! This looks good modulo a couple small things below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! And thanks for your patience getting this one 100% over the finish line!
https://github.com/fitzgen/bumpalo/runs/7239112120?check_suite_focus=true#step:5:126 Looks like |
@fitzgen Should be good now, I ran the tests on |
Looks like CI is still failing: https://github.com/fitzgen/bumpalo/runs/7286188380?check_suite_focus=true#step:5:342
|
@fitzgen I think this might be a nightly breakage as I ran this test on upstream main with nightly and experienced a failure |
The test passes on this PR and upstream main on the nightly of July 10th 2022 but not July 11th 2022, which is today |
Toying with the latest nightly it seems that |
I asked on the rust zulip and it seems like there is a new requirement for |
I made an issue for the breakage with a tentative suggested change |
Hey @fitzgen , just pinging in case this slipped by your email 😃 |
I don't have time to look into the old tests at the moment, if you can get them passing and send a PR then I can review that. |
I think this should pass CI now if rebased |
Arenas can optionally keep track of an `allocation_limit` which is considered when allocating new chunks to an arena. No new `new` methods were added, and limits must be set after the creation of a `Bump` arena. Limits can be changed on the fly and are only enforced when attempting to allocate new chunks. No changes were made to the errors returned by operations like `alloc` because of backwards compatibility.
Clarify that allocation limits are per `Bump` arena Co-authored-by: Nick Fitzgerald <[email protected]>
ChunkFooter now tracks the allocated bytes, reducing the operation of retrieving the allocated bytes to a field lookup instead of a linear walk of the chunk list.
Co-authored-by: Nick Fitzgerald <[email protected]>
Co-authored-by: Nick Fitzgerald <[email protected]>
To ensure that allocations do not exceed the allocation limit imposed on a `Bump`, the logic for calculating the final size of an allocated chunk was factored out into a separate function so that the limit enforcing code higher up in the control flow could more strictly enforce the allocation limit, as previously this code did not have visibility into the final size of an allocation request. To ensure that allocations in arenas with small limits could succeed, `alloc_layout_slow` was changed to allow bypassing the minimum chunk size for new chunks when trying to allocate in new arenas when the limit was lower than the default minimum chunk size.
When allocating new chunks, the memory layout for new chunks was calculated twice, once for the code checking if the new chunk would violate allocation limits and once for the actual code allocating the new chunk. This change caches the calculated new chunk memory layout inbetween these steps to avoid the extra calculation. This change means that `new_chunk` must be marked `unsafe` as before this change `new_chunk` could guarantee that the memory details of a new chunk were safe (correct alignment, size, etc.), now that it is not responsible for calculating these details, callers can cause unsoundness through misuse of `new_chunk`.
Seems like it does! |
Thanks! And thanks for your patience! |
I had a great time working on this PR, thanks for your work reviewing it and maintaining this crate! 😄 |
Implements #135
Arenas can optionally keep track of an
allocation_limit
which isconsidered when allocating new chunks to an arena.
The limit is only checked in
alloc_layout_slow
andnew_chunk
, which means itshould have a low performance overhead as the limit is only checked when trying to hit
the global allocator.
To further avoid performance concerns,
ChunkFooter
has been refactored to keeptrack of the total allocated bytes, allowing
Bump::allocated_bytes
to avoidperforming a walk of the chunk list.
No new
new
methods were added, and limits must be set after thecreation of a
Bump
arena. Limits can be changed on the fly and areonly enforced when attempting to allocate new chunks.
No changes were made to the errors returned by operations like
alloc
because of backwards compatibility.
I'm new to open source contributions and library development so sorry if there's anything obvious I missed.