Keep vector aligned to page size [duplicate] - rust

I'd like to align my heap memory to a specific alignment boundary. This boundary is known at compilation time. Anyhow, the Box abstraction doesn't allow me to specify a certain alignment. Writing my own Box-abstraction doesn't feel right too, because all of the Rust ecosystem uses Box already. What is a convenient way to achieve alignment for heap allocations?
PS: In my specific case I need page alignments.

If nightly is acceptable, the Allocator api provides a fairly convenient way to do this:
#![feature(allocator_api)]
use std::alloc::*;
use std::ptr::NonNull;
struct AlignedAlloc<const N: usize>;
unsafe impl<const N: usize> Allocator for AlignedAlloc<N> {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
Global.allocate(layout.align_to(N).unwrap())
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
Global.deallocate(ptr, layout.align_to(N).unwrap())
}
}
fn main() {
let val = Box::new_in(3, AlignedAlloc::<4096>);
let ptr: *const u8 = &*val;
println!(
"val:{}, alignment:{}",
val,
1 << (ptr as usize).trailing_zeros()
);
}
Playground
If you wanted you could also add support for using other allocators or choosing the value dynamically.
Edit: Come to think of it, this approach can also be used in stable via the GlobalAlloc trait and the #[global_allocator] attribute, but setting an allocator that way forces all allocations to be aligned to that same alignment, so it would be fine if you needed to ensure a relatively small alignment, but it is probably not a good idea for a page boundary alignment.
Edit 2: switched from using the System allocator to the Global allocator, because that's bundled in with the allocater_api feature, and it is a more sensible and predictable default.

Depending on what data you want to align, you can use repr (align) to specify the alignment of a type. You can either use that directly on your data or use a wrapper struct.
For example, the following will always be aligned on 16 bytes (whether on the heap, on the stack or inside another struct):
#[repr (C, align (16))]
struct AlignedBuffer([u8; 32]);

Posting an alternative answer just in case folks are curious. I'm not a Rust export but this seems to work:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=4c2d9c936e2755d1fc835d1e4663ec8b
#![feature(allocator_api)]
use std::{
alloc::{Layout, alloc_zeroed, handle_alloc_error}
};
const PAGE_SIZE: usize = 8192;
const NUM_PAGES: usize = 1000;
#[derive(Debug)]
struct Pool {
frames: Box<[[u8; PAGE_SIZE]; NUM_PAGES]>
}
impl Pool {
pub fn new() -> Self {
Self {
frames: unsafe {
let layout = Layout::from_size_align(NUM_PAGES * PAGE_SIZE, PAGE_SIZE).unwrap();
let ptr = alloc_zeroed(layout);
if ptr.is_null() {
handle_alloc_error(layout);
}
let ptr = ptr as *mut [[u8; PAGE_SIZE]; NUM_PAGES];
Box::from_raw(ptr)
},
}
}
}

Related

How to allocate buffer for C library call

The question is not new and there are two approaches, as far as I can tell:
Use Vec<T>, as suggested here
Manage the heap memory yourself, using std::alloc::alloc, as shown here
My question is whether these are indeed the two (good) alternatives.
Just to make this perfectly clear: Both approaches work. The question is whether there is another, maybe preferred way. The example below is introduced to identify where use of Vec is not good, and where other approaches therefore may be better.
Let's state the problem: Suppose there's a C library that requires some buffer to write into. This could be a compression library, for example. It is easiest to have Rust allocate the heap memory and manage it instead of allocating in C/C++ with malloc/new and then somehow passing ownership to Rust.
Let's go with the compression example. If the library allows incremental (streaming) compression, then I would need a buffer that keeps track of some offset.
Following approach 1 (that is: "abuse" Vec<T>) I would wrap Vec and use len and capacity for my purposes:
/// `Buffer` is basically a Vec
pub struct Buffer<T>(Vec<T>);
impl<T> Buffer<T> {
/// Create new buffer of length `len`
pub fn new(len: usize) -> Self {
Buffer(Vec::with_capacity(len))
}
/// Return length of `Buffer`
pub fn len(&self) -> usize {
return self.0.len()
}
/// Return total allocated size of `Buffer`
pub fn capacity(&self) -> usize {
return self.0.capacity()
}
/// Return remaining length of `Buffer`
pub fn remaining(&self) -> usize {
return self.0.capacity() - self.len()
}
/// Increment the offset
pub fn increment(&mut self, by:usize) {
unsafe { self.0.set_len(self.0.len()+by); }
}
/// Returns an unsafe mutable pointer to the buffer
pub fn as_mut_ptr(&mut self) -> *mut T {
unsafe { self.0.as_mut_ptr().add(self.0.len()) }
}
/// Returns ref to `Vec<T>` inside `Buffer`
pub fn as_vec(&self) -> &Vec<T> {
&self.0
}
}
The only interesting functions are increment and as_mut_ptr.
Buffer would be used like this
fn main() {
// allocate buffer for compressed data
let mut buf: Buffer<u8> = Buffer::new(1024);
loop {
// perform C function call
let compressed_len: usize = compress(some_input, buf.as_mut_ptr(), buf.remaining());
// increment
buf.increment(compressed_len);
}
// get Vec inside buf
let compressed_data = buf.as_vec();
}
Buffer<T> as shown here is clearly dangerous, for example if any reference type is used. Even T=bool may result in undefined behaviour. But the problems with uninitialised instance of T can be avoided by introducing a trait that limits the possible types T.
Also, if alignment matters, then Buffer<T> is not a good idea.
But otherwise, is such a Buffer<T> really the best way to do this?
There doesn't seem to be an out-of-the box solution. The bytes crate comes close, it offers a "container for storing and operating on contiguous slices of memory", but the interface is not flexible enough.
You absolutely can use a Vec's spare capacity as to write into manually. That is why .set_len() is available. However, compress() must know that the given pointer is pointing to uninitialized memory and thus is not allowed to read from it (unless written to first) and you must guarantee that the returned length is the number of bytes initialized. I think these rules are roughly the same between Rust and C or C++ in this regard.
Writing this in Rust would look like this:
pub struct Buffer<T>(Vec<T>);
impl<T> Buffer<T> {
pub fn new(len: usize) -> Self {
Buffer(Vec::with_capacity(len))
}
/// SAFETY: `by` must be less than or equal to `space_len()` and the bytes at
/// `space_ptr_mut()` to `space_ptr_mut() + by` must be initialized
pub unsafe fn increment(&mut self, by: usize) {
self.0.set_len(self.0.len() + by);
}
pub fn space_len(&self) -> usize {
self.0.capacity() - self.0.len()
}
pub fn space_ptr_mut(&mut self) -> *mut T {
unsafe { self.0.as_mut_ptr().add(self.0.len()) }
}
pub fn as_vec(&self) -> &Vec<T> {
&self.0
}
}
unsafe fn compress(_input: i32, ptr: *mut u8, len: usize) -> usize {
// right now just writes 5 bytes if there's space for them
let written = usize::min(5, len);
for i in 0..written {
ptr.add(i).write(0);
}
written
}
fn main() {
let mut buf: Buffer<u8> = Buffer::new(1024);
let some_input = 5i32;
unsafe {
let compressed_len: usize = compress(some_input, buf.space_ptr_mut(), buf.space_len());
buf.increment(compressed_len);
}
let compressed_data = buf.as_vec();
println!("{:?}", compressed_data);
}
You can see it on the playground. If you run it through Miri, you'll see it picks up no undefined behavior, but if you over-advertise how much you've written (say return written + 10) then it does produce an error that reading uninitialized memory was detected.
One of the reasons there isn't an out-of-the-box type for this is because Vec is that type:
fn main() {
let mut buf: Vec<u8> = Vec::with_capacity(1024);
let some_input = 5i32;
let spare_capacity = buf.spare_capacity_mut();
unsafe {
let compressed_len: usize = compress(
some_input,
spare_capacity.as_mut_ptr().cast(),
spare_capacity.len(),
);
buf.set_len(buf.len() + compressed_len);
}
println!("{:?}", buf);
}
Your Buffer type doesn't really add any convenience or safety and a third-party crate can't do so because it relies on the correctness of compress().
Is such a Buffer really the best way to do this?
Yes, this is pretty much the lowest cost ways to provide a buffer for writing. Looking at the generated release assembly, it is just one call to allocate and that's it. You can get tricky by using a special allocator or simply pre-allocate and reuse allocations if you're doing this many times (but be sure to measure since the built-in allocator will do this anyway, just more generally).

How can I align memory on the heap (in a box) in a convenient way?

I'd like to align my heap memory to a specific alignment boundary. This boundary is known at compilation time. Anyhow, the Box abstraction doesn't allow me to specify a certain alignment. Writing my own Box-abstraction doesn't feel right too, because all of the Rust ecosystem uses Box already. What is a convenient way to achieve alignment for heap allocations?
PS: In my specific case I need page alignments.
If nightly is acceptable, the Allocator api provides a fairly convenient way to do this:
#![feature(allocator_api)]
use std::alloc::*;
use std::ptr::NonNull;
struct AlignedAlloc<const N: usize>;
unsafe impl<const N: usize> Allocator for AlignedAlloc<N> {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
Global.allocate(layout.align_to(N).unwrap())
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
Global.deallocate(ptr, layout.align_to(N).unwrap())
}
}
fn main() {
let val = Box::new_in(3, AlignedAlloc::<4096>);
let ptr: *const u8 = &*val;
println!(
"val:{}, alignment:{}",
val,
1 << (ptr as usize).trailing_zeros()
);
}
Playground
If you wanted you could also add support for using other allocators or choosing the value dynamically.
Edit: Come to think of it, this approach can also be used in stable via the GlobalAlloc trait and the #[global_allocator] attribute, but setting an allocator that way forces all allocations to be aligned to that same alignment, so it would be fine if you needed to ensure a relatively small alignment, but it is probably not a good idea for a page boundary alignment.
Edit 2: switched from using the System allocator to the Global allocator, because that's bundled in with the allocater_api feature, and it is a more sensible and predictable default.
Depending on what data you want to align, you can use repr (align) to specify the alignment of a type. You can either use that directly on your data or use a wrapper struct.
For example, the following will always be aligned on 16 bytes (whether on the heap, on the stack or inside another struct):
#[repr (C, align (16))]
struct AlignedBuffer([u8; 32]);
Posting an alternative answer just in case folks are curious. I'm not a Rust export but this seems to work:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=4c2d9c936e2755d1fc835d1e4663ec8b
#![feature(allocator_api)]
use std::{
alloc::{Layout, alloc_zeroed, handle_alloc_error}
};
const PAGE_SIZE: usize = 8192;
const NUM_PAGES: usize = 1000;
#[derive(Debug)]
struct Pool {
frames: Box<[[u8; PAGE_SIZE]; NUM_PAGES]>
}
impl Pool {
pub fn new() -> Self {
Self {
frames: unsafe {
let layout = Layout::from_size_align(NUM_PAGES * PAGE_SIZE, PAGE_SIZE).unwrap();
let ptr = alloc_zeroed(layout);
if ptr.is_null() {
handle_alloc_error(layout);
}
let ptr = ptr as *mut [[u8; PAGE_SIZE]; NUM_PAGES];
Box::from_raw(ptr)
},
}
}
}

How do I force Rust to allocate a big object on the heap without using a Vec? [duplicate]

In this code...
struct Test { a: i32, b: i64 }
fn foo() -> Box<Test> { // Stack frame:
let v = Test { a: 123, b: 456 }; // 12 bytes
Box::new(v) // `usize` bytes (`*const T` in `Box`)
}
... as far as I understand (ignoring possible optimizations), v gets allocated on the stack and then copied to the heap, before being returned in a Box.
And this code...
fn foo() -> Box<Test> {
Box::new(Test { a: 123, b: 456 })
}
...shouldn't be any different, presumably, since there should be a temporary variable for struct allocation (assuming compiler doesn't have any special semantics for the instantiation expression inside Box::new()).
I've found Do values in return position always get allocated in the parents stack frame or receiving Box?. Regarding my specific question, it only proposes the experimental box syntax, but mostly talks about compiler optimizations (copy elision).
So my question remains: using stable Rust, how does one allocate structs directly on the heap, without relying on compiler optimizations?
As of Rust 1.39, there seems to be only one way in stable to allocate memory on the heap directly - by using std::alloc::alloc (note that the docs state that it is expected to be deprecated). It's reasonably unsafe.
Example:
#[derive(Debug)]
struct Test {
a: i64,
b: &'static str,
}
fn main() {
use std::alloc::{alloc, dealloc, Layout};
unsafe {
let layout = Layout::new::<Test>();
let ptr = alloc(layout) as *mut Test;
(*ptr).a = 42;
(*ptr).b = "testing";
let bx = Box::from_raw(ptr);
println!("{:?}", bx);
}
}
This approach is used in the unstable method Box::new_uninit.
It turns out there's even a crate for avoiding memcpy calls (among other things): copyless. This crate also uses an approach based on this.
You seem to be looking for the box_syntax feature, however as of Rust 1.39.0 it is not stable and only available with a nightly compiler. It also seems like this feature will not be stabilized any time soon, and might have a different design if it ever gets stabilized.
On a nightly compiler, you can write:
#![feature(box_syntax)]
struct Test { a: i32, b: i64 }
fn foo() -> Box<Test> {
box Test { a: 123, b: 456 }
}
Is there a way to allocate directly to the heap without box?
No. If there was, it wouldn't need a language change.
People tend to avoid this by using the unstable syntax indirectly, such as by using one of the standard containers which, in turn, uses it internally.
See also:
How to allocate arrays on the heap in Rust 1.0?
Is there any way to allocate a standard Rust array directly on the heap, skipping the stack entirely?
What does the box keyword do?
What is the <- symbol in Rust?
I recently had the same problem. Based on the answers here and other places, I wrote a simple function for heap allocation:
pub fn unsafe_allocate<T>() -> Box<T> {
let mut grid_box: Box<T>;
unsafe {
use std::alloc::{alloc, dealloc, Layout};
let layout = Layout::new::<T>();
let ptr = alloc(layout) as *mut T;
grid_box = Box::from_raw(ptr);
}
return grid_box;
}
This will create a region in memory automatically sized after T and unsafely convince Rust that that memory region is an actual T value. The memory may contain arbitrary data; you should not assume all values are 0.
Example use:
let mut triangles: Box<[Triangle; 100000]> = unsafe_allocate::<[Triangle; 100000]>();

How to create a pool of contigous memory across multiple structs in Rust?

I'm trying to create a set of structs in Rust that use a contiguous block of memory. E.g:
<------------ Memory Pool -------------->
[ Child | Child | Child | Child ]
These structs:
may each contain slices of the pool of different sizes
should allow access to their slice of the pool without any blocking operations once initialized (I intend to access them on an audio thread).
I'm very new to Rust but I'm well versed in C++ so the main hurdle thus far has been working with the ownership semantics - I'm guessing there's a trivial way to achieve this (without using unsafe) but the solution isn't clear to me. I've written a small (broken) example of what I'm trying to do:
pub struct Child<'a> {
pub slice: &'a mut [f32],
}
impl Child<'_> {
pub fn new<'a>(s: &mut [f32]) -> Child {
Child {
slice: s,
}
}
}
pub struct Parent<'a> {
memory_pool: Vec<f32>,
children: Vec<Child<'a>>,
}
impl Parent<'_> {
pub fn new<'a>() -> Parent<'a> {
const SIZE: usize = 100;
let p = vec![0f32; SIZE];
let mut p = Parent {
memory_pool: p,
children: Vec::new(),
};
// Two children using different parts of the memory pool:
let (lower_pool, upper_pool) = p.memory_pool.split_at_mut(SIZE / 2);
p.children = vec!{ Child::new(lower_pool), Child::new(upper_pool) };
return p; // ERROR - p.memory_pool is borrowed 2 lines earlier
}
}
I would prefer a solution that doesn't involve unsafe but I'm not entirely opposed to using it. Any suggestions would be very much appreciated, as would any corrections on how I'm (mis?)using Rust in my example.
Yes, it's currently impossible (or quite hard) in Rust to contain references into sibling data, for example, as you have here, a Vec and slices into that Vec as fields in the same struct. Depending on the architecture of your program, you might solve this by storing the original Vec at some higher-level of your code (for example, it could live on the stack in main() if you're not writing a library) and the slice references at some lower level in a way that the compiler can clearly infer it won't go out of scope before the Vec (doing it in main() after the Vec has been instantiated could work, for example).
This is the perfect use case for an arena allocator. There are quite a few. The following demonstration uses bumpalo:
//# bumpalo = "2.6.0"
use bumpalo::Bump;
use std::mem::size_of;
struct Child1(u32, u32);
struct Child2(f32, f32, f32);
fn main() {
let arena = Bump::new();
let c1 = arena.alloc(Child1(1, 2));
let c2 = arena.alloc(Child2(1.0, 2.0, 3.0));
let c3 = arena.alloc(Child1(10, 11));
// let's verify that they are indeed continuous in memory
let ptr1 = c1 as *mut _ as usize;
let ptr2 = c2 as *mut _ as usize;
let ptr3 = c3 as *mut _ as usize;
assert_eq!(ptr1 + size_of::<Child1>(), ptr2);
assert_eq!(ptr1 + size_of::<Child1>() + size_of::<Child2>(), ptr3);
}
There are caveats, too. The main concern is of course the alignment; there may be some padding between two consecutive allocations. It is up to you to make sure that doesn't gonna happen if it is a deal breaker.
The other is allocator specific. The bumpalo arena allocator used here, for example, doesn't drop object when itself gets deallocated.
Other than that, I do believe a higher level abstraction like this will benefit your project. Otherwise, it'll just be pointer manipulating c/c++ disguised as rust.

Is there a way to allocate directly to the heap without using the unstable box syntax? [duplicate]

In this code...
struct Test { a: i32, b: i64 }
fn foo() -> Box<Test> { // Stack frame:
let v = Test { a: 123, b: 456 }; // 12 bytes
Box::new(v) // `usize` bytes (`*const T` in `Box`)
}
... as far as I understand (ignoring possible optimizations), v gets allocated on the stack and then copied to the heap, before being returned in a Box.
And this code...
fn foo() -> Box<Test> {
Box::new(Test { a: 123, b: 456 })
}
...shouldn't be any different, presumably, since there should be a temporary variable for struct allocation (assuming compiler doesn't have any special semantics for the instantiation expression inside Box::new()).
I've found Do values in return position always get allocated in the parents stack frame or receiving Box?. Regarding my specific question, it only proposes the experimental box syntax, but mostly talks about compiler optimizations (copy elision).
So my question remains: using stable Rust, how does one allocate structs directly on the heap, without relying on compiler optimizations?
As of Rust 1.39, there seems to be only one way in stable to allocate memory on the heap directly - by using std::alloc::alloc (note that the docs state that it is expected to be deprecated). It's reasonably unsafe.
Example:
#[derive(Debug)]
struct Test {
a: i64,
b: &'static str,
}
fn main() {
use std::alloc::{alloc, dealloc, Layout};
unsafe {
let layout = Layout::new::<Test>();
let ptr = alloc(layout) as *mut Test;
(*ptr).a = 42;
(*ptr).b = "testing";
let bx = Box::from_raw(ptr);
println!("{:?}", bx);
}
}
This approach is used in the unstable method Box::new_uninit.
It turns out there's even a crate for avoiding memcpy calls (among other things): copyless. This crate also uses an approach based on this.
You seem to be looking for the box_syntax feature, however as of Rust 1.39.0 it is not stable and only available with a nightly compiler. It also seems like this feature will not be stabilized any time soon, and might have a different design if it ever gets stabilized.
On a nightly compiler, you can write:
#![feature(box_syntax)]
struct Test { a: i32, b: i64 }
fn foo() -> Box<Test> {
box Test { a: 123, b: 456 }
}
Is there a way to allocate directly to the heap without box?
No. If there was, it wouldn't need a language change.
People tend to avoid this by using the unstable syntax indirectly, such as by using one of the standard containers which, in turn, uses it internally.
See also:
How to allocate arrays on the heap in Rust 1.0?
Is there any way to allocate a standard Rust array directly on the heap, skipping the stack entirely?
What does the box keyword do?
What is the <- symbol in Rust?
I recently had the same problem. Based on the answers here and other places, I wrote a simple function for heap allocation:
pub fn unsafe_allocate<T>() -> Box<T> {
let mut grid_box: Box<T>;
unsafe {
use std::alloc::{alloc, dealloc, Layout};
let layout = Layout::new::<T>();
let ptr = alloc(layout) as *mut T;
grid_box = Box::from_raw(ptr);
}
return grid_box;
}
This will create a region in memory automatically sized after T and unsafely convince Rust that that memory region is an actual T value. The memory may contain arbitrary data; you should not assume all values are 0.
Example use:
let mut triangles: Box<[Triangle; 100000]> = unsafe_allocate::<[Triangle; 100000]>();

Resources