5个版本

使用旧Rust 2015

0.1.4 2016年1月4日
0.1.3 2015年8月9日
0.1.2 2015年5月7日
0.1.1 2015年5月7日
0.1.0 2015年5月7日

#410 in 内存管理

Download history 19/week @ 2024-03-11 17/week @ 2024-03-18 7/week @ 2024-03-25 26/week @ 2024-04-01 15/week @ 2024-04-08 15/week @ 2024-04-15 12/week @ 2024-04-22 13/week @ 2024-04-29 10/week @ 2024-05-06 19/week @ 2024-05-13 18/week @ 2024-05-20 34/week @ 2024-05-27 34/week @ 2024-06-03 31/week @ 2024-06-10 30/week @ 2024-06-17 27/week @ 2024-06-24

每月128次下载
abomonation中使用

MIT许可证

14KB
159

recycler

一个用于回收拥有内存类型的Rust库

Recycler提供了Recycler特质和几个实现。每个Recycler对象都能够回收其关联类型Item的项,并使用回收的项重新创建引用项的所有权副本。

pub trait Recycler : Default {
    type Item;
    fn recycle(&mut self, item: Self::Item);
    fn recreate(&mut self, other: &Self::Item) -> Self::Item;
}

默认的TrashRecycler只是将参数传递给recycle,并将参数克隆到recreate中。然而,对于具有所有权的类型的智能回收器可以解构项并存储其任何所有权的内存,然后使用存储的内存来重新创建项。

例如,VecRecycler<R>为可回收类型的向量做了这件事

// A recycler for vectors and their contents
pub struct VecRecycler<R: Recycler> {
    pub recycler: R,
    stash: Vec<Vec<R::Item>>,
}

impl<R: Recycler> Recycler for VecRecycler<R> {
    type Item = Vec<R::Item>;
    // recycles vec contents and then stashes the vec
    fn recycle(&mut self, mut vec: Vec<R::Item>) {
        while let Some(x) = vec.pop() {
            self.recycler.recycle(x)
        }
        self.stash.push(vec);
    }
    // pops a stashed vector and then recreates each element
    fn recreate(&mut self, other: &Vec<R::Item>) -> Vec<R::Item> {
        let mut vec = self.stash.pop().unwrap_or(Vec::new());
        for elem in other.iter() {
            vec.push(self.recycler.recreate(elem));
        }
        vec
    }
}

虽然回收可能看起来只是为了返回所有内存,但真正的目的是这些回收器能够使用类似于标准分配的模式返回拥有的内存。你可能会写一些类似

#[bench]
fn allocate_vec_vec_str(bencher: &mut Bencher) {
    bencher.iter(|| {
        let mut v1 = Vec::with_capacity(10);
        for _ in 0..10 {
            let mut v2 = Vec::with_capacity(10);
            for _ in 0..10 {
                v2.push(("test!").to_owned());
            }
            v1.push(v2);
        }
        v1
    });
}

的东西,但现在你可以写类似的东西(不,不是一样的)

#[bench]
fn recycler_vec_vec_str(bencher: &mut Bencher) {
    let mut r1 = make_recycler::<Vec<Vec<String>>>();
    bencher.iter(|| {
        let v = { // scope the borrow of r1
            let (mut v1, r2) = r1.new();
            for _ in 0..10 {
                let (mut v2, r3) = r2.new();
                for _ in 0..10 {
                    v2.push(r3.new_from("test!"));
                }
                v1.push(v2);
            }
            v1
        };
        r1.recycle(v);
    });
}

你这样做的原因是,如果你运行那些基准测试,你会看到类似的数据

test allocate_vec_vec_str ... bench:       3,494 ns/iter (+/- 1,128)
test recycler_vec_vec_str ... bench:       1,709 ns/iter (+/- 643)

如果你做更少的格式化,只将一些u64数据放入向量中,你会看到类似的效果

test allocate_vec_vec_u64 ... bench:         267 ns/iter (+/- 49)
test recycler_vec_vec_u64 ... bench:         145 ns/iter (+/- 26)

主要的缺点是,你可能会得到比需要的更多内存的向量,并且内存可能会在回收器中保留很长时间。我几乎添加了一个clear方法,但如果你想要这样做,只需创建一个新的回收器并覆盖旧的即可。

注意:之前版本的这些数字对于 allocate 变体看起来更糟糕,因为它们使用了 Vec::new() 而不是 Vec::with_capacity(10),后者正确地设置了分配大小并避免了复制。

重新创建

如果你出于某种原因发现你经常被赋予对象的引用,并且需要一个快速的克隆(例如,在 Abomonation 中使用 decodeverify),则 recreate 方法旨在使其变得无痛。上面的基准测试变为

#[bench]
fn recreate_vec_vec_str(bencher: &mut Bencher) {
    let mut recycler = make_recycler::<Vec<Vec<String>>>();
    let data = vec![vec!["test!".to_owned(); 10]; 10];
    bencher.iter(|| {
        let record = recycler.recreate(&data);
        recycler.recycle(record);
    });
}

如果你比较使用 recreate 和仅使用 clone,你会看到以下数字

test clone_vec_vec_str    ... bench:       2,906 ns/iter (+/- 774)
test recreate_vec_vec_str ... bench:       1,773 ns/iter (+/- 625)

test clone_vec_vec_u64    ... bench:         344 ns/iter (+/- 134)
test recreate_vec_vec_u64 ... bench:         157 ns/iter (+/- 42)

谢谢!

如果您有任何热点提示或建议,特别是关于宏或语法扩展,这将使结构体等自动推导回收器,我会洗耳恭听。任何其他友好的评论或贡献也受欢迎。

无运行时依赖