2 个不稳定版本
0.2.0 | 2024年1月5日 |
---|---|
0.1.0 | 2023年12月29日 |
#2131 in Rust 模式
13KB
Soapy
Soapy 使得操作结构化数组内存布局变得简单。Vec
示例
use soapy::{Soa, Soapy};
[derive(Soapy, Debug, Clone, Copy, PartialEq)]
struct Example {
foo: u8,
bar: u16,
}
let elements = [Example { foo: 1, bar: 2 }, Example { foo: 3, bar: 4 }];
let mut soa: Soa<_> = elements.into_iter().collect();
// The index operator is not possible, but we can use nth:
*soa.nth_mut(0).foo += 10;
// We can get the fields as slices as well:
let slices = soa.slices();
assert_eq!(slices.foo, &[11, 3][..]);
assert_eq!(slices.bar, &[2, 4][..]);
for (actual, expected) in soa.iter().zip(elements.iter()) {
assert_eq!(&expected.bar, actual.bar);
}
什么是 SoA?
以下类型说明了 AoS 和 SoA 之间的区别
[(u8, u64)] // AoS
([u8], [u64]) // Soa
与 AoS 相比,SoA 将每个字段分割成自己的数组。这有几个优点
- 不需要在相同类型实例之间填充。在上面的示例中,每个 AoS 元素需要 128 位来满足内存对齐要求,而每个 SoA 元素只需 72 位。这意味着更好的缓存局部性和更低的内存使用。
- SoA 更适合向量化。在 SoA 中,可以直接将多个值批量加载到 SIMD 寄存器中,而不是在不同的 SIMD 寄存器之间移动结构字段。
SoA 是面向数据设计中的一种流行技术。Andrew Kelley 提供了一次精彩的 演讲,描述了 SoA 和其他面向数据设计模式如何帮助他在 Zig 编译器中减少了 39% 的wall clock time。
请注意,SoA 并不是在所有情况下都能带来性能提升。SoA 最适合以下情况
- 顺序访问是常见的访问模式
- 您经常只访问或修改字段的一个子集
始终最好为您的用例进行性能分析。
派生
Soapy 提供了 Soapy
派生宏来自动生成结构体的 SoA 兼容性。当派生 Soapy 时,会创建几个新的结构体。由于 SoA 数据的存储方式,迭代器和获取器通常会返回这些类型而不是原始结构体。如果某个结构体 Example
的每个字段类型为 F
,我们的新结构体具有相同的字段但不同的类型
结构体 | 字段类型 | 用途 |
---|---|---|
ExampleRawSoa |
*mutF |
Soa 的低级、不安全接口 |
ExampleRef |
&F |
.iter() 、nth() 、.get() |
示例RefMut |
&可变F |
.iter_mut() 、nth_mut 、get_mut() |
示例Slices |
&[F] |
.slices() 、.get() |
示例SlicesMut |
&可变 [F] |
.slices_mut() 、.get_mut() |
这些类型也被包含在Soapy
特质的关联类型中。通常,你不需要考虑这些,因为[Soa
]会自动获取它们。然而,由于它们继承了派生结构的可见性,你应该考虑是否将它们包含在你的模块的pub
项中。
比较
soa_derive
soa_derive
将每个字段都当作自己的Vec
。因此,每个字段的长度、容量和分配是分开管理的。相比之下,Soapy为每个Soa
管理单个分配。这使用更少的空间,并允许集合更有效地增长和收缩。soa_derive
还为每个结构生成一个新的集合类型,而Soapy生成最小的、底层的接口,并使用通用的Soa
类型作为实现的大部分。这提供了更多类型系统灵活性、更少的代码生成和更易于访问的文档。
soa-vec
而soa-vec
只能在nightly编译,Soapy也可以在稳定版编译。而不是使用derive宏,soa-vec
使用宏生成八个具有固定元组大小的SoA类型的静态副本。
进展
Soa
-
depup
/dedup_by
/dedup_by_key
-
drain
-
extend_from_slice
/extend_from_within
-
extract_if
-
leak
-
retain
-
try_reserve
/try_reserve_exact
-
dedup_by
/dedup_by_key
-
resize
/resize_with
-
splice
-
split_off
SoaSlice
-
select_nth_unstable
/select_nth_unstable_by
/select_nth_unstable_by_key
-
sort
/sort_by
/sort_by_key
/sort_by_cached_key
-
sort_unstable
/sort_unstable_by
/sort_unstable_by_key
/sort_unstable_by_cached_key
-
binary_search
/binary_search_by
/binary_search_by_key
-
is_sorted
/is_sorted_by
/is_sorted_by_key
-
chunks
/rchunks
-
chunks_exact
/rchunks_exact
-
first
/last
-
rotate_left
/rotate_right
-
split
/rsplit
/splitn
-
split_at
/split_first
/split_last
-
交换
-
与切片交换
-
按组分类
-
包含
-
在内部复制
-
fill
/fill_with
-
重复
-
反转