You can also implement BumpUp() with only two conditionals and no overflow like this (in C, because I don't know Rust, but the logic is identical): size_t alignment_needed = align - (ptr & (align - 1)); if (alignment_needed > SIZE_MAX - size) return nullptr; size_t space_needed = size + alignment_needed; size_t space_remaining = end - ptr; if (space_needed > space_remaining) return nullptr; char *result = ptr + align…
Always Bump Downwards (2019)
31–40 of 74 posts
Re: Always Bump Downwards (2019)
#32Re: Always Bump Downwards (2019)
#33You can also implement BumpUp() with only two conditionals and no overflow like this (in C, because I don't know Rust, but the logic is identical): size_t alignment_needed = align - (ptr & (align - 1)); if (alignment_needed > SIZE_MAX - size) return nullptr; size_t space_needed = size + alignment_needed; size_t space_remaining = end - ptr; if (space_needed > space_remaining) return nullptr; char *result = ptr + align…
Re: Always Bump Downwards (2019)
#34In the "bump up" version you could remove both the checked_add branches and replace them with a single check at the end, making the amount of branches the same. Quick example: https://godbolt.org/z/rdv4qnrs8 . *edited to update the example, realized I messed up the comparison logic.
I think this doesn't work because `aligned + size` may wrap all the way around into the valid region again. For example if aligned == ptr + 1, and size is usize::MAX, we will end up with new_ptr == ptr and the allocation will wrongly succeed.
Re: Always Bump Downwards (2019)
#35In the "bump up" version you could remove both the checked_add branches and replace them with a single check at the end, making the amount of branches the same. Quick example: https://godbolt.org/z/rdv4qnrs8 . *edited to update the example, realized I messed up the comparison logic.
That version is unsafe: what if size == 0xfff..fff and alignment is needed? You will end up with ptr edit : code moved to a toplevel comment
Re: Always Bump Downwards (2019)
#36I only recently learned about bump allocators. I was very confused as to how such an allocator could be useful. In reading the author's bumpalo crate [1] documentation, he cleared up my confusion. For one thing, this particular allocator is separate from the general allocator so the calling code can choose when to use the bump allocator. Quoting the crate doc: > [B]ump allocation [is] well-suited for phase-oriented a…
Re: Always Bump Downwards (2019)
#37Earlier quoted context omitted.
It's not integer overflow but pointer overflow. If you're doing small bump allocations and you're anywhere near pointer overflow, it means you're way out of bounds already; the bump allocations you already made were wrong. You need to check whether the allocation increment is out of the zone from which you're allocating, and you need that no matter which direction you go. If the requests are small, you will hit the e…
I would definitely be in favor of an even faster `SmallBump` variant which assumes that the allocations are small w.r.t. usize::MAX for some additional speed. I also wouldn't mind the a default "fast" bump allocator library to do all tricks it can without sacrificing safety.
Re: Always Bump Downwards (2019)
#38Earlier quoted context omitted.
That version is unsafe: what if size == 0xfff..fff and alignment is needed? You will end up with ptr edit : code moved to a toplevel comment
No allocator can be expected to allocate usize::MAX, so it doesn't really matter.
Re: Always Bump Downwards (2019)
#39I've heard that the Hotspot JVM uses a bump allocator but don't know the details. I'm sure it's heavily optimized though, so I'm curious about how this compares.
Hard-coding the alignment simplifies many questions (especially if you can guarantee the allocation size being a multiple of the alignment, at which point it becomes a complete non-issue).
And if you have an upper bound on the requested allocation size, the integer overflow checks can be dropped too (e.g. in a Java "new int[n]", "n" is an int, which has a max value of 2^31-1, which can safely be added to a bump pointer, provided it's further than that away from the address space end (which it's gonna be due to the kernel reserving the upper half of the address space)).
And for constant-size objects, your entire bump allocator can become just
if (ptr > precomputedEndMinusObjectSize) fallback();
result = ptr;
ptr += size;Re: Always Bump Downwards (2019)
#40Earlier quoted context omitted.
This sounds like it would make the alloc logic much more complicated and branch-y, defeating the purpose of bumping down anyway, unless your implying some compile-time way to do this.
No, the idea is that you manually make some allocations downward from the top and some allocations upward from the bottom. The bumping code is as simple as in the unidirectional case. The tricky part is choosing in a way that puts you noticeably ahead of the unidirectional allocator re: what problems you can solve, without putting excessive mental load on yourself. I've found a pattern of "long-lived allocations on o…
Incidentally, this is what Knuth does in TeX, if I understand correctly: http://mirrors.ctan.org/info/knuth-pdf/tex/tex.pdf#page=43 (section 116):
> The mem array is divided into two regions that are allocated separately, but the dividing line between these two regions is not fixed; they grow together until finding their “natural” size in a particular job. Locations less than or equal to lo_mem_max are used for storing variable-length records consisting of two or more words each. […] Locations greater than or equal to hi_mem_min are used for storing one-word records…
(Different allocators are used for the two regions and neither seems to be a bump allocator, so it's probably not very relevant to this thread, but I was reminded of it so just sharing…)