17个版本
| 0.13.2 | 2024年5月22日 |
|---|---|
| 0.13.1 | 2023年10月16日 |
| 0.13.0 | 2023年5月12日 |
| 0.12.0 | 2022年4月11日 |
| 0.5.0 | 2017年12月21日 |
#338 在 #attributes
5,534 每月下载量
在 2 个crate中使用 (通过 soa_derive)
120KB
2K SLoC
Rust自动生成数组结构的工具
此crate提供了一个自定义 derive (#[derive(StructOfArray)]),可以从给定的结构 T 自动生成代码,允许用数组结构替换 Vec<T>。例如,以下代码
#[derive(StructOfArray)]
pub struct Cheese {
pub smell: f64,
pub color: (f64, f64, f64),
pub with_mushrooms: bool,
pub name: String,
}
将生成一个类似于下面的 CheeseVec 结构
pub struct CheeseVec {
pub smell: Vec<f64>,
pub color: Vec<(f64, f64, f64)>,
pub with_mushrooms: Vec<bool>,
pub name: Vec<String>,
}
它还将生成与 Vec<Cheese> 相同的函数,以及一些辅助结构: CheeseSlice、CheeseSliceMut、CheeseRef 和 CheeseRefMut,分别对应于 &[Cheese]、&mut [Cheese]、&Cheese 和 &mut Cheese。
由 StructOfArray 衍生的任何结构都将自动实现 StructOfArray trait。您可以使用 <Cheese as StructOfArray>::Type 代替显式命名的类型 CheeseVec。
如何使用它
将 #[derive(StructOfArray)] 添加到您想要生成数组结构版本的每个结构体中。如果您需要辅助结构体以派生额外的特性(如 Debug 或 PartialEq),可以在结构体声明中添加一个属性 #[soa_derive(Debug, PartialEq)]。
#[derive(Debug, PartialEq, StructOfArray)]
#[soa_derive(Debug, PartialEq)]
pub struct Cheese {
pub smell: f64,
pub color: (f64, f64, f64),
pub with_mushrooms: bool,
pub name: String,
}
如果您想向特定生成的结构体添加属性(例如在 CheeseVec 上添加 #[cfg_attr(test, derive(PartialEq))]),可以在结构体声明中添加属性 #[soa_attr(Vec, cfg_attr(test, derive(PartialEq)))]。
#[derive(Debug, PartialEq, StructOfArray)]
#[soa_attr(Vec, cfg_attr(test, derive(PartialEq)))]
pub struct Cheese {
pub smell: f64,
pub color: (f64, f64, f64),
pub with_mushrooms: bool,
pub name: String,
}
将 soa_attr 的第一个参数映射到为 Cheese 生成的结构体
Vec=>CheeseVecSlice=>CheeseSliceSliceMut=>CheeseSliceMutRef=>CheeseRefRefMut=>CheeseRefMutPtr=>CheesePtrPtrMut=>CheesePtrMut
用法和API
所有生成的代码都包含一些生成的文档,因此您应该能够在您的crate上使用 cargo doc 并查看所有生成的结构和函数的文档。在大多数情况下,您可以将 Vec<Cheese> 替换为 CheeseVec,除了在向量中使用直接索引的代码和一些其他注意事项。
注意事项和限制
Vec<T> 的功能很大程度上依赖于引用和自动 解引用 功能,用于从 [T] 和索引中获取函数。但是,由这个 crate 生成的 SoA 向量(我们将其称为 CheeseVec,由 Cheese 结构体生成)无法实现 Deref<Target=CheeseSlice>,因为 Deref 需要返回一个引用,而 CheeseSlice 不是一个引用。同样的情况也适用于 Index 和 IndexMut 特性,它们无法返回 CheeseRef/CheeseRefMut。这意味着我们不能索引 CheeseVec,并且一些函数是重复的,或者需要调用 as_ref()/as_mut() 来更改使用的类型。
迭代
可以迭代 CheeseVec 中的值。
let mut vec = CheeseVec::new();
vec.push(Cheese::new("stilton"));
vec.push(Cheese::new("brie"));
for cheese in vec.iter() {
// when iterating over a CheeseVec, we load all members from memory
// in a CheeseRef
let typeof_cheese: CheeseRef = cheese;
println!("this is {}, with a smell power of {}", cheese.name, cheese.smell);
}
SoA 布局的一个主要优点是可以在迭代向量时只加载一些字段。为了做到这一点,可以手动选择所需的字段。
for name in &vec.name {
// We get referenes to the names
let typeof_name: &String = name;
println!("got cheese {}", name);
}
要同时迭代多个字段,可以使用 soa_zip! 宏。
for (name, smell, color) in soa_zip!(vec, [name, mut smell, color]) {
println!("this is {}, with color {:#?}", name, color);
// smell is a mutable reference
*smell += 1.0;
}
嵌套数组结构
为了在另一个 SoA 结构体内部嵌套数组结构体,可以使用 #[nested_soa] 属性。
例如,以下代码
#[derive(StructOfArray)]
pub struct Point {
x: f32,
y: f32,
}
#[derive(StructOfArray)]
pub struct Particle {
#[nested_soa]
point: Point,
mass: f32,
}
将生成如下结构的结构体
pub struct PointVec {
x: Vec<f32>,
y: Vec<f32>,
}
pub struct ParticleVec {
point: PointVec, // rather than Vec<Point>
mass: Vec<f32>
}
所有辅助结构体也将被嵌套,例如 PointSlice 将嵌套在 ParticleSlice 中。
文档
请参阅 http://lumol.org/soa-derive/soa_derive_example/ 以了解一个小示例和所有生成代码的文档。
基准测试
以下是一些在我的机器上的简单基准测试结果
running 10 tests
test aos_big_do_work_100k ... bench: 415,315 ns/iter (+/- 72,861)
test aos_big_do_work_10k ... bench: 10,087 ns/iter (+/- 219)
test aos_big_push ... bench: 50 ns/iter (+/- 10)
test aos_small_do_work_100k ... bench: 93,377 ns/iter (+/- 1,106)
test aos_small_push ... bench: 3 ns/iter (+/- 1)
test soa_big_do_work_100k ... bench: 93,719 ns/iter (+/- 2,793)
test soa_big_do_work_10k ... bench: 9,253 ns/iter (+/- 103)
test soa_big_push ... bench: 39 ns/iter (+/- 13)
test soa_small_do_work_100k ... bench: 93,301 ns/iter (+/- 1,765)
test soa_small_push ... bench: 4 ns/iter (+/- 1)
基准测试存在 soa(结构数组)和 aos(结构数组)版本的相同代码,使用一个小(24 字节)和一个大(240 字节)的结构体。
您可以在自己的系统上运行相同的基准测试,通过克隆此存储库并运行 cargo bench。
许可和贡献
此 crate 在 MIT 或 Apache 许可下分发,由您选择。欢迎贡献,请在讨论您的更改之前打开一个问题!
感谢 @maikklein 提出的初始想法:https://maikklein.github.io/soa-rust/
依赖项
~260–710KB
~17K SLoC