在 Rust 中用零值初始化结构向量

Initialize a vector of struct with zero values in Rust

struct MyStruct {
    v1: u32,
    v2: u64,
}

type MyVector = Vec<MyStruct>;
impl Default for MyStruct {
    fn default() -> Self {
        Self {
            v1: 0,
            v2: 0,
        }
    }
}

fn init_my_vec() {
    let size = 1000;
    let mut my_vec: MyVector = Vec::with_capacity(size);
    (0..size).for_each(|_| my_vec.push(MyStruct::default()))
}
let usize_vec: Vec<usize> = vec![0; 1000];
// is faster than
let mut usize_vec: Vec<usize> = Vec::with_capacity(1000);
for i in 0..1000 {
    usize_vec.push(0);
}

问题

  1. 我对矢量初始化速度的看法是否正确?由于用0填充是特殊指令,使用迭代器比使用宏慢。
  2. 有没有什么方法可以安全又快速的初始化0值struct的vector
  3. 或者我应该使用 不安全 代码,比如制作空字节并将其转换为向量?

速度测量大约 Question 1

const VEC_SIZE: usize = 10_000;

fn init_with_iter() -> u128 {
    let start = Instant::now();
    let mut usize_vec: Vec<usize> = Vec::with_capacity(VEC_SIZE);
    for i in 0..VEC_SIZE {
        usize_vec.push(0);
    }
    start.elapsed().as_micros()
}

fn init_with_macro() -> u128 {
    let start = Instant::now();
    let _: Vec<usize> = vec![0; VEC_SIZE];
    start.elapsed().as_micros()
}

生成向量 10,000 次的平均时间是


测速约Question 3

我认为使用不安全函数 mem::zeroed 比任何其他函数都快一点

const VEC_SIZE: usize = 10_000;

fn init_with_iter() -> u128 {
    let start = Instant::now();
    let mut my_vec: MyVector = Vec::with_capacity(VEC_SIZE);
    for _ in 0..VEC_SIZE {
        my_vec.push(MyStruct::default());
    }
    start.elapsed().as_micros()
}

fn init_with_macro() -> u128 {
    let start = Instant::now();
    let _: MyVector = vec![MyStruct::default(); VEC_SIZE];
    start.elapsed().as_micros()
}

fn init_with_zeroed() -> u128 {
    let start = Instant::now();
    let _: MyVector = unsafe { vec![std::mem::zeroed(); VEC_SIZE] };
    start.elapsed().as_micros()
}

生成向量 1,000 次的平均时间是

这是您的三种方法的 criterion 基准:

use criterion::{black_box, criterion_group, criterion_main, Criterion};

criterion_group!(
    benches,
    init_structs_with_iter,
    init_structs_with_macro,
    init_structs_with_unsafe
);
criterion_main!(benches);

const N_ITEMS: usize = 1000;

#[allow(unused)]
#[derive(Debug, Clone)]
struct MyStruct {
    v1: u32,
    v2: u64,
}

impl Default for MyStruct {
    fn default() -> Self {
        Self { v1: 0, v2: 0 }
    }
}

fn init_structs_with_iter(c: &mut Criterion) {
    c.bench_function("structs: with_iter", |b| {
        b.iter(|| {
            let mut my_vec = Vec::with_capacity(N_ITEMS);
            (0..my_vec.capacity()).for_each(|_| my_vec.push(MyStruct::default()));
            black_box(my_vec);
        })
    });
}

fn init_structs_with_macro(c: &mut Criterion) {
    c.bench_function("structs: with_macro", |b| {
        b.iter(|| {
            let my_vec = vec![MyStruct::default(); N_ITEMS];
            black_box(my_vec);
        })
    });
}

fn init_structs_with_unsafe(c: &mut Criterion) {
    c.bench_function("structs: with_unsafe", |b| {
        b.iter(|| {
            let my_vec: Vec<MyStruct> = vec![unsafe { std::mem::zeroed() }; N_ITEMS];
            black_box(my_vec);
        })
    });
}

结果:

structs: with_iter      time:   [1.3857 us 1.3960 us 1.4073 us]                                
structs: with_macro     time:   [563.30 ns 565.30 ns 567.32 ns]                                 
structs: with_unsafe    time:   [568.84 ns 570.09 ns 571.49 ns]                                  

vec![] 宏似乎是最快的(也是最简洁易读的)。

如您所见,时间以 纳秒 为单位测量,因此虽然迭代器版本慢 2-3 倍,但在实践中并不重要。优化结构的零初始化是您可以做的最不重要的事情 - 您最多可以节省 1 微秒 ;)

PS:这些时间包括内存分配和释放时间