1 个不稳定版本
0.1.0 | 2023 年 7 月 4 日 |
---|
#2821 在 Rust 模式
13KB
145 行
ortho-vec-derive
一个派生宏,根据您的结构体创建正交向量,以允许更好的 CPU 缓存使用。
动机
使用大型、复杂的结构体 Vec
,可能会减慢运行时速度。当遍历向量时,如果未使用结构体的所有字段,这种情况就会发生。
例如,让我们想象我们有以下 struct
struct LargeStruct {
speed: f32,
location: f32,
more_data: [u64; 10],
}
在内存中,Vec<LargeStruct>
将看起来像这样
4B 4B 80B 4B 4B 80B
[[speed] [location] [more_data] [speed] [location] [more_data]]
^ ^
| |
first object starts here second object starts here
现在我们想要遍历这个 Vec
中的对象,并根据速度更新位置
struct LargeStruct {
speed: f32,
location: f32,
more_data: [u64; 10],
}
let mut large_structs = vec![LargeStruct {speed: -1.2, location: 7.3, more_data: [0; 10]}];
for large_struct in large_structs.iter_mut() {
large_struct.location += large_struct.speed;
}
CPU 将读取内存中 .speed
和 .location
的位置,但也会缓存内存中的下一个字节。缓存了多少字节可以基于您的特定 CPU 而变化。这意味着,我们将读取大量的非必需内存 (.more_data
) 到缓存中。反过来,对于下一个对象的 .speed
的读取,我们必须再次直接进入 RAM,这比缓存慢 10-100 倍。
为了防止这些缓存未命中发生并减慢程序速度,通常会使用结构体中的每个字段的 Vec
,这样所有 Vec
中的相同索引都表示相同的对象。
以这种方式编写代码并管理几个 Vec
可能会有些复杂,并且需要大量样板代码。
OrthoVec
派生宏试图解决这些问题。
支持的 API
Vec
into_ortho()
- 将Vec
转换为其正交版本。
ortho-Vec
所有这些都是与 Vec
方法相同的
iter()
iter_mut()
into_iter()
len()
push()
pop()
clear()
反转()
shrink_to_fit()
唯一的注意事项是,当使用 iter_mut()
遍历时,你会得到一个包含对每个内部 struct
的 &mut
指针的 Vec
。要使用它,你需要通过添加一个 *
前缀来解引用。
示例
任何命名的结构体(目前如此 - 未来应该支持类似元组的类型)
#[derive(OrthoVec)]
use ortho_vec_derive::prelude::*;
#[derive(OrthoVec)]
struct WeirdStruct<'a, T: Send>
where
T: std::fmt::Debug,
{
a: i32,
b: &'a f32,
c: T,
}
fn main () {
let mut ortho_vec = vec![
WeirdStruct {
a: 3,
b: &4.2,
c: "nice",
},
WeirdStruct {
a: 6,
b: &24.2,
c: "hello",
},
]
.into_ortho();
ortho_vec.push(WeirdStruct {
a: 7,
b: &8.123,
c: "push",
});
for ws in ortho_vec.iter_mut() {
*ws.a += 3;
}
for ws in ortho_vec.iter() {
println!("b = {:?}", ws.b);
}
}
结果
结果可能因用例和平台而异。运行一个小的基准测试(该测试在包的仓库中),我们可以看到大约6倍的速度提升,这是在1M/10M时,但在大小为10K/100K时,我们也可以看到大约2-3倍的速度提升。建议你只需尝试将代码的两个版本相互基准测试,这样你可以确保这在你的情况下是有效的。
注意
目前宏不能用于函数内部定义的结构体。
依赖项
~265–710KB
~17K SLoC