rust 如何创建一个有效的不可变树,与父指针

wnvonmuf  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(169)

我正在构造一个“scenegraph”,它是Shape节点的分层数据结构(例如球体、立方体、网格等,示例代码中未显示)。Shape可以拥有零个或多个子Shape示例。这形成了一棵树,树被完全构建,然后不可变地传递给一组工作线程,这些线程基于这个场景图来渲染图像的各个部分。场景图一旦构建好就不需要更改,因此线程只需要读取权限。
x1c 0d1x的数据
我的问题涉及到scenegrape的构造。最初,我有一个自己的子节点。即。一个形状拥有所有的孩子。因此,我没有使用指针或引用。这工作正常,直到我需要添加父指针。
Shape的每个子节点现在必须维护一个指向其父节点的指针。(因为最终场景图是在线程之间共享的),这让我们使用Weak ptr到父节点以避免循环问题。但这意味着主拥有关系,其中Weak拥有子节点,现在也必须是Arc,这个指针比父指针被遍历了很多很多次。
据我所知,Arc的特性仅在构建此数据结构并移交给线程池期间是必需的。在渲染这些Arc指针时是不幸的开销。此时数据结构被认为是不可变的,因此不再需要Arc的特性。它们仅在构建数据结构期间需要,支持创建Weak父指针。
有趣的是,我无法在 safe 代码中找到一种方法来将子对象设置为Shape * 所拥有,并在子对象中设置父对象指针。这是一种双向链表,我知道这在Rust中很难。最后我不得不使用一行unsafe来设置下面Shape::set_parent()中的指针:

use std::sync::{Arc, Weak};

pub trait LocalIntersect {
    fn local_intersect(&self);
}

#[derive(Debug, Default)]
pub struct Shape {
    id: usize,
    members: Vec<Arc<Shape>>,
    parent: Option<Weak<Shape>>,
}

impl Shape {
    fn new(id: usize) -> Self {
        Self { id, members: vec![], parent: None }
    }

    fn add(&mut self, shape: Arc<Shape>) {
        self.members.push(shape);
    }

    fn set_parent(&self, parent: Weak<Shape>) {
        let this = self as *const Shape as *mut Shape;
        unsafe {
            (*this).parent = Some(parent);
        }
    }

    fn set_parent_to_all(&self, parent_weak: Weak<Shape>) {
        for member in &self.members {
            member.set_parent(parent_weak.clone());
        }
    }
}

impl LocalIntersect for Shape {
    fn local_intersect(&self) {
        // For now, check if there is a parent and if so, print its ID
        // (Later, we'll combine parent transforms, but not now)
        if let Some(parent_weak) = &self.parent {
            if let Some(parent) = parent_weak.upgrade() {
                println!("local_intersect {}, parent {}", self.id, parent.id);
            }
        }
    }
}

fn main() {
    let mut parent_shape = Shape::new(0);
    let child_shape = Shape::new(1);

    let child_arc = Arc::new(child_shape);

    // Add child to the parent's group
    parent_shape.add(Arc::clone(&child_arc));

    // Wrap the parent shape in Arc
    let parent_arc = Arc::new(parent_shape);

    // Set the parent pointer of each child in the group
    parent_arc.set_parent_to_all(Arc::downgrade(&parent_arc));

    // Share across threads (example with a thread)
    let shared_node = Arc::clone(&child_arc);
    let thread = std::thread::spawn(move || {
        shared_node.local_intersect();
    });
    thread.join().unwrap();

    // Output is:
    //   local_intersect 1, parent 0
}

字符串
Rust Playground
考虑到性能是这个应用程序的重中之重,在Rust中有没有一种惯用的方法来处理这个问题?这是unsafe代码的好候选者吗?我应该删除ArcWeak,只使用普通的子所有权和父指针的原始指针吗?如果是这样,我以后如何处理删除这个数据结构?
我可以使用像id_treepetgraph这样的东西,但是在查找子对象或父对象时,它们也会带来开销。我真的只是想要一个指针,渲染器可以直接跟踪父对象,而不会影响非常常见的“get child”查找的性能。
我不是Rust或C/C++的新手,但我是编写unsafe Rust代码的新手。
编辑:例如,在一个C程序中,我只需要在渲染之前给所有的子对象添加一个原始的父对象指针,然后在释放任何对象之前将它们全部置空,这不会影响所有权或导致悬空指针问题。我正在寻找一个类似的过程在Rust中-只是一个漂亮的,简单的,快速的父对象指针在一个最终不可变的数据结构中,但没有鸡和蛋的问题。

9w11ddsr

9w11ddsr1#

如果你真的认为树一旦建立就不可变,也许你可以把所有的节点打包成一个向量,用索引代替引用。(也许这就是所谓的竞技场,不确定是否有什么微妙之处)。(我们从来不需要自己分配id)。在多线程上下文中,我们只需要将节点的向量作为一个整体共享/传递。
下面是根据这个想法稍微修改过的例子(我不理解local_intersect()函数,也许它没有做你想要的)。

use std::sync::Arc;

mod shape_tree {

    #[derive(Debug)]
    pub struct Shape {
        id: usize,
        members: Vec<usize>,
        parent_id: usize,
    }

    impl Shape {
        pub fn new(
            shapes: &mut Vec<Self>,
            parent_id: Option<usize>,
        ) -> usize {
            let id = shapes.len();
            shapes.push(Self {
                id,
                members: Vec::new(),
                parent_id: usize::MAX,
            });
            if let Some(parent_id) = parent_id {
                shapes[parent_id].members.push(id);
                shapes[id].parent_id = parent_id;
            };
            id
        }

        pub fn id(&self) -> usize {
            self.id
        }

        pub fn members(&self) -> &[usize] {
            &self.members
        }

        pub fn parent<'a>(
            &self,
            shapes: &'a [Self],
        ) -> Option<&'a Self> {
            if self.parent_id == usize::MAX {
                None
            } else {
                Some(&shapes[self.parent_id])
            }
        }
    }
}

pub trait LocalIntersect {
    fn local_intersect(
        &self,
        shapes: &[shape_tree::Shape],
    );
}

impl LocalIntersect for shape_tree::Shape {
    fn local_intersect(
        &self,
        shapes: &[shape_tree::Shape],
    ) {
        // For now, check if there is a parent and if so, print its ID
        // (Later, we'll combine parent transforms, but not now)
        if let Some(parent) = self.parent(shapes) {
            println!("local_intersect {}, parent {}", self.id(), parent.id());
        }
    }
}

fn main() {
    let mut shapes = Vec::new();
    let parent_id = shape_tree::Shape::new(&mut shapes, None);
    let child_id = shape_tree::Shape::new(&mut shapes, Some(parent_id));
    let shapes = Arc::new(shapes.into_boxed_slice()); // seal the vector

    let thread = std::thread::spawn({
        let shapes = Arc::clone(&shapes);
        move || {
            println!("in thread");
            shapes[child_id].local_intersect(&shapes);
        }
    });
    thread.join().unwrap();

    println!("back to main");
    shapes[child_id].local_intersect(&shapes);
}
/*
in thread
local_intersect 1, parent 0
back to main
local_intersect 1, parent 0
*/

字符串

相关问题