#struct #macro-derive #vec #derive #performance #macro

ortho_vec_derive

为了更好地使用 CPU 缓存而派生正交向量结构

1 个不稳定版本

0.1.0 2023 年 7 月 4 日

#2821Rust 模式

MIT 许可证

13KB
145

ortho-vec-derive

一个派生宏,根据您的结构体创建正交向量,以允许更好的 CPU 缓存使用。

Crates.io Last commit GitHub Workflow Status License: MIT

动机

使用大型、复杂的结构体 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