size, when rounded up to the nearest multiple of align,
must not overflow isize (i.e., the rounded value must be
less than or equal to isize::MAX).
In other words, we can create allocations with size smaller than alignment. And indeed the following code works:
let l = alloc::alloc::Layout::from_size_align(1, 4096).unwrap();
let p = unsafe { alloc::alloc::alloc_zeroed(l) };
unsafe { alloc::alloc::dealloc(p, l) }
Was it intentional or is it just an oversight? Initially, I was expecting for Layout::from_size_align to return an error in such case.
My immediate thought was "yes, it can sometimes be useful to have the alignment of an allocation be greater than its size".
On second thought, however, the only example I can think of is the C standard's requirement that malloc produce allocations which are aligned "so that [the pointer returned by malloc] may be assigned to a pointer to any type of object with a fundamental alignment requirement". This has been interpreted to mean that if the maximum alignment requirement of any type supported by the implementation is, say, 16, then malloc must always return pointers with alignment 16, even if the maximum alignment requirement of any type that would fit in the space you asked for is smaller. And I have never heard anyone say that they found this useful, but I have regularly heard people complain that it makes their program (that does lots of tiny allocations) waste a lot of heap space. So that's not a good example.
Note that there is also pad_to_align to resolve that difference. I don't know why it wasn't just required to have that padding in the first place though, so size would always be a multiple of align.
Maybe it's so extend_packed (still unstable) can build up a layout field-by-field and only add padding at the end?
It can be useful to align objects to cache lines or pages, even if they are smaller size. This way you know they don't unnecessarily cross cache line boundaries and that they don't share cache lines.
I think so. One use Layout is to incrementally build up the layout for a structure field-by-field—some of its methods are provided for precisely this purpose. In intermediate steps it necessarily does not have a natural size as the padding can still hold some small fields with weak alignment requirements: Layout::extend would be pointless if the self argument already needed to be a multiple of its align. It'd be a shame if Layout could not be used for this process. And as already remarked, due to the non-overflow requirements an allocator can quite transparently decide to pad these types if they don't want to support such layouts.
It's 'merely' a property of Rust types to be a multiple of their alignment, so that the type arithmetic with arrays works out to allow addressing into them, but layouts are a little more general. If you have a memory description system that makes that access only conditionally avaiable you can also go ahead and work with unpadded structs—just not as proper Rust type (but maybe as 'DST's of a sort, who knows, those rules are not fixed, no-one has defined what kind of provenance a reference to a DST really has).
I don’t have a reference to it now, but iirc jemalloc disagrees and only guarantees alignment up to allocation size. I think there might be a link to some discussion in the std::alloc::System implementation.
My understanding was that the jemalloc developers considered this an intentional deviation from a silly requirement, not that they disputed the requirement itself.
It's useful for Layout to be able to represent sub-layouts or prefixes or similar sometimes, even if it's questionably valuable whether ever passing such a thing to a real allocator is useful.
(I do wonder if maybe Layout should in retrospect have been called LayoutBuilder, with allocators actually taking a final thing instead that enforces other things like "non-zero size" and such.)