如何以方便的方式对齐堆上的内存(在一个盒子里)?
How can I align memory on the heap (in a box) in a convenient way?
我想将我的堆内存对齐到特定的对齐边界。这个边界在编译时是已知的。无论如何,Box
抽象不允许我指定特定的对齐方式。编写我自己的 Box
-抽象感觉也不对,因为所有 Rust 生态系统都已经使用 Box
。实现堆分配对齐的便捷方法是什么?
PS:在我的具体情况下,我需要页面对齐。
如果 nightly 是可以接受的,Allocator
api 提供了一种相当方便的方法来做到这一点:
#![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()
);
}
如果需要,您还可以添加对使用其他分配器或动态选择值的支持。
编辑:想想看,这种方法也可以通过 GlobalAlloc
特性和 #[global_allocator]
属性在稳定版中使用,但是以这种方式设置分配器会强制所有分配对齐到相同的对齐方式,因此如果您需要确保相对较小的对齐方式就可以了,但是对于页面边界对齐方式来说这可能不是一个好主意。
编辑 2:从使用 System
分配器切换到 Global
分配器,因为它与 allocater_api
功能捆绑在一起,并且它是一个更明智和可预测的默认值。
根据你要对齐的数据,你可以使用repr (align)
来指定一个类型的对齐方式。您可以直接在数据上使用它,也可以使用包装器结构。
例如,以下将始终按 16 字节对齐(无论是在堆上、堆栈上还是在另一个结构内):
#[repr (C, align (16))]
struct AlignedBuffer([u8; 32]);
我想将我的堆内存对齐到特定的对齐边界。这个边界在编译时是已知的。无论如何,Box
抽象不允许我指定特定的对齐方式。编写我自己的 Box
-抽象感觉也不对,因为所有 Rust 生态系统都已经使用 Box
。实现堆分配对齐的便捷方法是什么?
PS:在我的具体情况下,我需要页面对齐。
如果 nightly 是可以接受的,Allocator
api 提供了一种相当方便的方法来做到这一点:
#![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()
);
}
如果需要,您还可以添加对使用其他分配器或动态选择值的支持。
编辑:想想看,这种方法也可以通过 GlobalAlloc
特性和 #[global_allocator]
属性在稳定版中使用,但是以这种方式设置分配器会强制所有分配对齐到相同的对齐方式,因此如果您需要确保相对较小的对齐方式就可以了,但是对于页面边界对齐方式来说这可能不是一个好主意。
编辑 2:从使用 System
分配器切换到 Global
分配器,因为它与 allocater_api
功能捆绑在一起,并且它是一个更明智和可预测的默认值。
根据你要对齐的数据,你可以使用repr (align)
来指定一个类型的对齐方式。您可以直接在数据上使用它,也可以使用包装器结构。
例如,以下将始终按 16 字节对齐(无论是在堆上、堆栈上还是在另一个结构内):
#[repr (C, align (16))]
struct AlignedBuffer([u8; 32]);