使用数组存储树(或图)
-
我是 Rust 新手,最近在尝试实现蒙特卡洛树搜索算法。我还在努力学习如何用 Rust 的惯用写法,但遇到的第一个难题(当然)就是如何实现树。
我知道有 Box 和 RC 之类的工具,但我觉得用数组更简单。比如:
Struct node { value: some value, children: Vec<usize>, } let myTree: [node; array_size] = initialization...;就我而言,我把代码设计成运行固定次数,这样我就可以把数组初始化为正确的大小(这也让它驻留在栈上)。但我也可以用向量(vec)替换数组,让它动态调整大小。当时,这一切看起来都很合理,我认为我的做法很符合 Rust 的惯用风格。
但我后来看到一些帖子(比如这篇)表明这种设计模式不推荐使用?
我看到有人抱怨未初始化/失效/空索引,这也是我之前遇到的问题,需要想办法解决。不过最简单的解决方法是用
Vec<Option<usize>>来处理空值(而且在我的情况下,整棵树会被一次性释放,所以失效节点的影响不大)。这看起来和Vec<Option<&node>>基本一样,而指针方案就是用指针,所以我看不出切换回真正的指针能带来多大的优势。我还看到有人抱怨需要自己“清理”树状结构。如果这棵树是为了存储频繁添加和删除的项,我能理解这会是个问题。但对于(例如)蒙特卡罗搜索(MCTS)来说,我知道整棵树会在搜索结束后一次性构建并丢弃,因此强制所有项具有相同的生命周期反而是一个优点。
此外,将其放入数组或向量中可以保持所有数据集中在一起,我认为这有利于提高缓存命中率?(如果我能预测正确的数组大小,它甚至可以放在栈上)
我还看到了链接的帖子,其中提到了 slab、slotmap 和 handy 之类的东西,它们似乎都是“将尺寸转换为向量”这一想法的迭代,并针对特定的痛点进行了优化。
这种设计模式真的糟糕到我应该尽量避免吗?还是说它是一种合理的模式,正确的选择取决于具体的用例?(例如,元素的生命周期是否真的相似,是否真的需要进行如此多的借用以至于常规引用过于繁琐等等……)
IronCalc:Rust 电子表格引擎 v0.7.1 发布
大家好,
我们刚刚发布了用 Rust 编写的开源电子表格引擎的新版本。它发展迅速,我非常欢迎更多人加入合作。 此次版本更新的主要目标是国际化,但在成为一个成熟的产品之前,还有大量工作要做。
我认为这是一个学习 Rust 的绝佳项目 :) 而且它很有潜力。
这是 GitHub 地址:
https://github.com/ironcalc/IronCalc
这是概念验证:
https://app.ironcalc.com/
欢迎提出反馈意见、新想法和讨论。
Minarrow:一种用于 Rust 的 Apache Arrow 内存布局,编译时间小于 2 秒
我一直在开发一个列式数据库,该数据库优先考虑快速编译和直接类型化访问,而不是功能完整性。
为什么还要再建一个 Arrow 库?
Arrow-rs 非常出色,但编译需要 3-5 分钟,而且到处都需要向下转型。我想要的是一个:
- 编译耗时小于 1.5 秒(干净编译),增量编译耗时小于 0.15 秒。
- 提供直接的类型化访问,无需动态分发*(例如,as_any().downcast_ref())。*
- 仍然可以通过 C 数据接口与 Arrow 进行互操作
- 简单快捷——无生态系统负担
您可能感兴趣的设计选择:
- 使用双枚举分发代替 trait 对象:Array -> NumericArray -> IntegerArray。使用符合人体工程学的宏来避免样板代码。
- 编译器将所有内容内联,基准测试显示,对于 1000 个元素的访问,耗时约为 88ns,而 arrow-rs 的耗时约为 147ns。
- 使用 Vec64(64 字节对齐)进行缓冲区抽象,用于 SIMD 和 SharedBuffer,用于具有写时复制语义的零拷贝借用。
- MemFd 支持 Linux 上的跨进程零拷贝
- 使用 portable_simd 作为算术内核*(通过配套的* simd-kernels crate)。
- Parquet 和 IPC 支持,包括内存映射读取(通过同级 lightstream crate)
权衡取舍:
- 不支持嵌套类型(结构体、列表、联合体) - 专注于扁平列式数据
- 需要 nightly 版本才能使用 portable_simd 和 allocator_api
- 不如箭矢那样久经沙场
如果您从事高性能数据系统编程工作,并有任何反馈或其他相关用例,我很乐意听听。
谢谢
From 日报小组 时光
社区学习交流平台订阅:
Rustcc论坛: 支持rss 微信公众号:Rust语言中文社区
评论区
写评论还没有评论