Recently, I wanted to allocate a vector of u64
s aligned to a 128-byte boundaries so I could use AVX512F functions. My first pass of a function to copy any Vec<u64>
to an aligned Vec<u64>
:
fn copy_to_aligned(inp: Vec<u64>) -> Vec<u64> {
// note: when the returned vec is freed, the alignment / layout information
// will be lost. The internal memory will be freed using the alignment
// of a u64.
let aligned_layout = Layout::from_size_align(inp.len() * size_of::<u64>(), 128)
.unwrap();
let new_vec = unsafe {
let new_vec_mem = alloc_zeroed(aligned_layout) as *mut u64;
copy_nonoverlapping(inp.as_ptr(), new_vec_mem, inp.len());
Vec::from_raw_parts(new_vec_mem, inp.len(), inp.len())
};
return new_vec;
}
I now realize that this code is wrong: while the new vector is created with aligned memory and the appropriate content, when the new vector is dropped, the memory will be de-allocated with the alignment of u64
. The documentation for from_raw_parts
mentions this pitfall as well.
1) Why does the alignment of an allocation matter when I am freeing that allocation? For example, in C, I can free any pointer by simply calling free
-- the alignment of the allocation is irrelevant. What is special about Rust's allocator that it needs to know the alignment?
2) What is the correct way to allocate this aligned vector? Should I use a boxed slice? Write my own struct with a custom Drop
implementation that gets the alignment right? Other sources suggest creating a struct and specifying the alignment, and then making a Vec
of those structs (causing the de-allocation to use the right layout). But even with repr(C)
, this won't ensure that all of my u64
s are nice and densely packed next to each other, will it? I imagine arbitrary padding could be added to the struct.