如何在Rust中跨多个结构创建连续内存池?

时间:2019-08-23 10:59:38

标签: rust

我正在尝试在Rust中创建一组使用连续内存块的结构。例如:

<------------ Memory Pool -------------->
[  Child  |  Child  |  Child  |  Child  ]

这些结构:

  • 每个可能包含不同大小的池的切片

  • 应该允许在初始化后立即进行任何阻止操作(我打算在音频线程上访问它们),而无需任何阻塞操作。

我对Rust还是很陌生,但是我精通C ++,所以到目前为止,主要的障碍是使用所有权语义-我猜想有一个简单的方法可以实现这一目标(不使用不安全的方法),但是解决方案对我来说还不清楚。我已经写了一个小例子(破碎的例子):

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
    }
}

我希望解决方案不涉及不安全因素,但我并不完全反对使用它。我们将不胜感激任何建议,以及对我在示例中使用Rust的任何更正。

2 个答案:

答案 0 :(得分:0)

是的,当前在Rust中不可能(或相当困难)包含对同级数据的引用,例如,像这里一样,Vec并切成Vec作为同一字段中的字段struct。根据程序的体系结构,您可以通过将原始Vec存储在代码的更高级别来解决此问题(例如,如果您不将原始main()存储在Vec中的堆栈中,则可以解决此问题)编写库)和切片引用在较低级别,以便编译器可以清楚地推断出它不会超出main()之前的范围(在{{1之后,在Vec中这样做}}已实例化,例如可以使用。

答案 1 :(得分:0)

这是一个竞技场分配器的完美用例。有很多。以下演示使用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);
}

也有一些警告。当然,主要关注的是alignment;在两个连续的分配之间可能会有一些填充。您有责任确保这不会破坏交易。

另一个是分配器特定的。例如,此处使用的凹凸小子竞技场分配器在自身被释放时不会drop对象。

除此之外,我相信这样的更高层次的抽象将使您的项目受益。否则,它将只是伪装成锈的指针操作c / c ++。