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
=>CheeseVec
Slice
=>CheeseSlice
SliceMut
=>CheeseSliceMut
Ref
=>CheeseRef
RefMut
=>CheeseRefMut
Ptr
=>CheesePtr
PtrMut
=>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