2个版本
0.1.1 | 2019年11月25日 |
---|---|
0.1.0 | 2019年11月25日 |
#485 in 内存管理
10KB
188 行
RealBox:让Box再次伟大!
背景:隐藏的内存拷贝
众所周知,Box<T>
首先在栈上分配内存,并将初始化的结构体复制到堆上。因此,在嵌入式设备上创建大型boxed对象会导致栈溢出而不是堆分配器OOM。
copyless
希望通过直接调用分配原语来解决它,并导致使用ptr::write
,其定义如下
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn write<T>(dst: *mut T, src: T) {
intrinsics::move_val_init(&mut *dst, src)
}
intrinsics
是一个“内联符号”,编译器后端可以识别它。注释中提到
if let Some(sym::move_val_init) = intrinsic {
// `move_val_init` has "magic" semantics - the second argument is
// always evaluated "directly" into the first one.
然而,这并不总是正确的。在调试构建中,rustc仍然会触发memcpy
448a: 48 8b 7c 24 38 mov 0x38(%rsp),%rdi
448f: 48 89 ce mov %rcx,%rsi
4492: ba 94 01 00 00 mov $0x194,%edx
4497: 48 89 44 24 30 mov %rax,0x30(%rsp)
449c: e8 e7 f9 ff ff callq 3e88 <memcpy@plt>
我的结论是:move_val_init
的保证取决于优化,这可能无法由Rust保证。
解决方案
关键区别在于这个crate提供的API需要
impl<T> RealBox<T, Global> {
pub fn heap_init<F>(initialize: F) -> Box<T>
where
F: Fn(&mut T),
{
unsafe {
let mut t = Self::new_in(Global).into_box();
initialize(t.as_mut());
t
}
}
}
一个初始化器 Fn(&mut T)
,并且不依赖于move_val_init
。
用法
#[derive(Debug)]
struct Obj {
x: u32,
y: f64,
a: [u8; 4],
}
let stack_obj = Obj {
x: 12,
y: 0.9,
a: [0xff, 0xfe, 0xfd, 0xfc],
};
let heap_obj = RealBox::<Obj>::heap_init(|mut t| {
t.x = 12;
t.y = 0.9;
t.a = [0xff, 0xfe, 0xfd, 0xfc]
});