-
Notifications
You must be signed in to change notification settings - Fork 0
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
Reduce size of Vec
#18
Comments
It's also possible to get
As follows:
struct AllocatorChunkFooter {
allocator: *const Allocator,
/* other fields */
}
struct Vec<T> {
ptr: NonNull<T>,
len: u32,
capacity: u32,
}
const CHUNK_ALIGNMENT_BITS: u32 = 5;
const CAPACITY_MASK: u32 = u32::MAX >> CHUNK_ALIGNMENT_BITS;
const CHUNK_ALIGNMENT_SHIFT: u32 = 32 - CHUNK_ALIGNMENT_BITS;
const ALLOCATOR_FIELD_MASK: usize = usize::MAX - std::mem::size_of::<AllocatorChunkFooter>() + 1
+ std::mem::offset_of!(AllocatorChunkFooter, allocator);
impl<T> Vec<T> {
pub fn len(&self) -> u32 { self.len }
pub fn capacity(&self) -> u32 { self.capacity & CAPACITY_MASK }
pub fn push(&mut self, value: T) {
if self.len() == self.capacity() { self.grow_for_push(); }
self.ptr.as_ptr().add(len).write(value);
self.len += 1;
}
#[cold]
fn grow_for_push(&mut self) {
let ptr = self.ptr.as_ptr() as *const u8;
let power = (self.capacity >> CHUNK_ALIGNMENT_SHIFT) as usize;
let chunk_end_minus_one_addr = ptr as usize | ((1 << power) - 1);
let allocator_ptr_addr = chunk_end_minus_one_addr & ALLOCATOR_FIELD_MASK;
let allocator_ptr = ptr.add(allocator_ptr_addr - ptr as usize) as *const Allocator;
let allocator: &Allocator = &*allocator_ptr;
// Reallocate `Vec` using `allocator`
}
} Notes:
|
Alternatively, could record This would allow 8 sizes of allocator chunks: 16 MiB, 32 MiB, 64 MiB, 128 MiB, 256 MiB, 512 MiB, 1 GiB, 2 GiB. That's probably sufficient. However, I think using bits in |
If using bottom 3 bits of 128KiB, 512 KiB, 2 MiB, 8 MiB, 32 MiB, 128 MiB, 512 MiB, 2 GiB I still think it's better to use spare bits in There is also the option to use high bits of |
I forked https://github.com/oxc-project/oxc-bumpalo, changing |
Yes, I realised we'd need to combine all the fields into a single struct: // 24 bytes
struct ArenaVec<'a, T> {
ptr: NonNull<T>,
len: u32,
capacity: u32,
allocator: &'a Allocator,
} Splitting it into I don't think combining all the methods into 1 struct is so terrible - it just means moving a lot of the code about, rather than a fundamental change. And we can still break up the implementation into multiple files to make it manageable. But yeah, it's not trivial. |
Related to this, Bumpalo is planning to remove the Vec types in favor of std types as soon as the API is stable. So it would be wise to start using our custom vectors if we wish to keep control over our types. Here I tried to offer to implement a feature-gated Vec32 type fitzgen/bumpalo#256 and found out that they are not too keen on keeping their vector types(Which makes sense, In general, you want to use |
This comment was marked as resolved.
This comment was marked as resolved.
I doubt I don't actually think it'll be too hard to copy Also relevant: |
Vec
is currently 32 bytes. It consists of 4 x 8-bytes fields:Vec
's contents (NonNull<u8>
)usize
)usize
)*const Allocator
)Reduce to 24 bytes
Make length and capacity fields
u32
.This is pretty easy.
Reduce to 16 bytes
If arena allocator always created chunks aligned on 4 GiB boundaries, and stored chunk metadata in a header at start of chunks, the pointer field could do double-duty.
Pointer field would be a pointer to the
Vec
's contents. But also if you zero out the bottom 32 bits it would give you a pointer to the chunk metadata. The chunk metadata would contain a pointer back to theAllocator
, with which you can grow/reallocate theVec
etc.This would require replacing bumpalo with a custom allocator.
The text was updated successfully, but these errors were encountered: