如何加速我的自定义结构中的 Rust 克隆方法?

rus*_*ner 0 python struct clone rust pyo3

我在 Rust 中有结构体,其中包含以下字段及其类型:

\n
    \n
  • 五个i8
  • \n
  • 六架F32
  • \n
  • 一布尔
  • \n
  • 一个大小为 4 的 Vec
  • \n
  • 一个大小为 4x20x7 的 Vec<Vec<Vec>>
  • \n
  • 一个大小为 4x20 的 Vec<Vec>
  • \n
  • 一个 HashMap<i8, Vec> 有 4 个键
  • \n
  • 一个大小从 0 到 30 变化的 Vec<(i8, i8)>
  • \n
\n

我的问题是如何加速这种结构的对象的克隆?\n现在我只使用

\n
fn py_make_copy(&self) -> PyResult<Self> {\n    Ok(self.clone())\n}\n
Run Code Online (Sandbox Code Playgroud)\n

它需要 4 到 6 \xc2\xb5s,这对我来说太慢了。我正在用 Python 测量它。\n不幸的是,我必须深度复制这些对象。

\n

我不知道这是否相关,但我使用

\n
#[pyclass(subclass)]\n#[derive(Clone)]\n
Run Code Online (Sandbox Code Playgroud)\n

定义此 Struct 和 #[pyo3(get)] 到所有字段时。

\n

我尝试使用 par_iter 并行克隆,但它的持续时间比 py_make_copy 长得多。

\n

Fin*_*nis 7

我试图重现你的说法,这就是我得到的:

use std::{collections::HashMap, hint::black_box, time::Instant};

#[derive(Clone)]
pub struct MyStruct {
    pub five_i8: [u8; 5],
    pub six_f32: [f32; 6],
    pub one_bool: bool,
    pub vec1: Vec<u8>,
    pub vec2: Vec<Vec<Vec<u8>>>,
    pub vec3: Vec<Vec<u8>>,
    pub hashmap: HashMap<i8, Vec<u8>>,
    pub vec4: Vec<(i8, i8)>,
}

fn main() {
    let mut s = MyStruct {
        five_i8: [42u8; 5],
        six_f32: [69.420; 6],
        one_bool: true,
        vec1: vec![42u8; 4],
        vec2: Default::default(),
        vec3: Default::default(),
        hashmap: Default::default(),
        vec4: vec![(1i8, 2i8); 30],
    };

    for i in 0..4 {
        let mut x = vec![];
        for _ in 0..20 {
            x.push(vec![1u8; 7]);
        }

        s.vec2.push(x);
        s.vec3.push(vec![42u8; 20]);
        s.hashmap.insert(i, vec![2u8; 10]);
    }

    // Blackbox to prevent optimization
    let s = black_box(s);

    let start = Instant::now();
    for _ in 0..10000 {
        let s2 = s.clone();
        black_box(s2);
    }
    let elapsed = start.elapsed();

    println!("Time: {} us", elapsed.as_micros() / 10000);
}
Run Code Online (Sandbox Code Playgroud)
$ cargo run --release
Time: 17 us
Run Code Online (Sandbox Code Playgroud)

现在为什么这么慢?答案是:堆分配。其他一切都非常快。

结构中唯一执行堆分配的是HashMapVec。每个VecHashMap都是一个堆分配。

那么让我们看看:

  • vec11分配
  • vec21 + 4 * (1 + 20) = 85分配
  • vec31 + 4 = 5分配
  • hashmap1 + 4 = 5分配
  • vec41分配

这是分配总数97

最简单的方法是将 更改Vec<Vec<Vec<>>>为单个扁平化Vec. 这将减少vec2vec3减少到单个分配,以及分配的总数9

像这样:

$ cargo run --release
Time: 17 us
Run Code Online (Sandbox Code Playgroud)
$ cargo run --release
Time: 1 us
Run Code Online (Sandbox Code Playgroud)