0.1.5 |
|
---|---|
0.1.4 |
|
#46 在 #ref
40KB
303 代码行
成员引用向量
此软件包允许您创建一个“临时”引用 Vec,它持久其分配的内存,并且可以作为成员变量存储。
例如,假设您想要准备一个 Vec,其中包含要发送到其他代码片段的缓冲区引用。但问题是这将分配内存,这可能导致性能问题(或者在实时代码的情况下,任何类型的分配都是不可接受的)。
let mut buffers: Vec<&[f32; 256]> = Vec::new();
// "Push" allocates memory here
buffers.push(&my_buffer_1);
buffers.push(&my_buffer_2);
i_want_a_slice_of_references(&buffers[..]);
这里的常规解决方案是使用 smallvec
,它在堆栈上分配内存。实际上,如果您的情况得到了解决,请使用它而不是此软件包。
let mut buffers: SmallVec<[&[f32; 256]; 8]> = SmallVec::new();
// Does not allocated memory anymore!
buffers.push(&my_buffer_1);
buffers.push(&my_buffer_2);
i_want_a_slice_of_references(&buffers[..]);
但是,如果我们意外地推入了比我们在 SmallVec 中定义的 8 个槽位更多的槽位,那么它将再次分配内存。如果最大槽位数量在编译时未知,您有两个选择
选项 1 是仅分配大量槽位,并希望它永远不会超过容量。然而,如果它变得太大,可能会溢出堆栈,并且如果大多数时间只使用少数槽位,那么具有不寻常大堆栈大小的函数可能会有性能损失。
选项 2 是使用此软件包。以下是它的工作原理
由于 Rust 不喜欢自我引用的结构体,MemberRefVec
必须包含类型 Vec<&'static T>
。但您的数据很可能没有静态生命周期。
这里的关键技巧是这个结构体中的一个函数,它将这个 Vec<&'static T>
转换为 Vec<&'a T>
,然后将其发送到闭包。此操作是安全的,因为在这个 Vec 在发送到闭包之前总是被清除,这意味着永远不会从它那里读取未初始化的数据并导致未定义的行为。它还通过在闭包作用域结束时自动清除 Vec 来避免自我引用的结构体。
use member_ref_vec::MemberRefVec;
// Pre-allocate some capacity in a non-performance critical part
// of your code. Also please note the lack of the `&` symbol in
// the type parameter here. This is *not* allocating 1024
// buffers with 256 f32s, This is still just allocating 1024
// references to buffers.
let mut buffer_refs: MemberRefVec<[f32; 256]> = MemberRefVec::with_capacity(1024);
// (You may also use `MemberRefVecMut` for mutable references.)
// -- In the performance-critical part of your code: ---------
buffer_refs.as_empty_vec_of_refs(|buffers| {
// Does not allocated memory! (as long as you don't push more
// elements than what was allocated in the non-performance
// critical part of your code)
buffers.push(&my_buffer_1);
buffers.push(&my_buffer_2);
i_want_a_slice_of_references(&buffers[..]);
});
安全注意事项
请注意,该代码的安全性尚未经过实战测试。我的简短测试显示它似乎是安全的,但请自行承担风险。此crate的大部分功能也可以使用 smallvec
实现,因此在考虑使用此crate之前,请考虑使用后者。
此crate目前假设 Vec<&'static T>
在内存中与 Vec<&'a T>
具有完全相同的布局(以及 Vec<&'static mut T>
在内存中总是与 Vec<&'a mut T>
具有完全相同的布局)其中 T: 'static + Sized
。如果您知道这个假设是否正确,请与我联系。