rust - 如何在结构中具有可重用的Vec <RwLockReadGuard>以避免分配?

标签 rust lifetime

我的算法使用Vec<RwLockReadGuard<..>>处理数据。该算法被重复调用,并且我不想每次都被分配Vec,如果可以在处理结束时对其进行clear()并将其存储在与数据处理相关的相同结构。但是,RwLockReadGuard的生存期短于保持结构的可能生存期。由于我只在数据处理函数内部使用Vec,而在它外部始终为空,因此我仍可以某种方式将其存储在struct中吗?是否有 crate 或成语可能会对我有所帮助?

显示问题的最小可重现示例位于问题的底部。

如果每次都分配Vec,它将是这样的:

#[derive(Clone)]
pub struct T;

pub fn process_ts(ts: &[T]) {
    unimplemented!();
}

struct Process {
    locked_ts: Vec<RwLock<Vec<T>>>,
}

impl Process {
    pub fn process(&self) {
        let mut ts: Vec<T> = Vec::with_capacity(self.locked_ts.len());

        let guards: Vec<RwLockReadGuard<Vec<T>>> = self
            .locked_ts
            .iter()
            .map(|locked_t| locked_t.read().unwrap())
            .collect();

        let n = guards.iter().map(|guard| guard.len()).min().unwrap();

        for i in 0..n {
            ts.clear();
            for t in &guards {
                ts.push(t[i].clone());
                process_ts(&ts);
            }
        }
    }
}

我对这种解决方案不满意的是,每次调用Process::process时,都会分配ts: Vec<T>guards: Vec<RwLockReadGuard<Vec<T>>>。我可以摆脱ts:

struct ProcessReuseTs {
    locked_ts: Vec<RwLock<Vec<T>>>,
    reusable_ts: Vec<T>,
}

impl ProcessReuseTs {
    pub fn process(&mut self) {
        let guards: Vec<RwLockReadGuard<Vec<T>>> = self
            .locked_ts
            .iter()
            .map(|locked_t| locked_t.read().unwrap())
            .collect();

        let n = guards.iter().map(|guard| guard.len()).min().unwrap();

        for i in 0..n {
            self.reusable_ts.clear();
            for t in &guards {
                self.reusable_ts.push(t[i].clone());
                process_ts(&self.reusable_ts);
            }
        }
    }
}

但是,如何提取guards呢?

use std::sync::{RwLock, RwLockReadGuard};

#[derive(Clone)]
pub struct T;

pub fn process_ts(ts: &[T]) {
    unimplemented!();
}

struct ProcessReuseBoth {
    locked_ts: Vec<RwLock<Vec<T>>>,
    reusable_ts: Vec<T>,
    reusable_guards: Vec<RwLockReadGuard<Vec<T>>>,
}

impl ProcessReuseBoth {
    pub fn process(&mut self) {
        self.reusable_guards.clear();
        self.reusable_guards.extend(
            self.locked_ts
                .iter()
                .map(|locked_t| locked_t.read().unwrap()),
        );

        let n = self
            .reusable_guards
            .iter()
            .map(|guard| guard.len())
            .min()
            .unwrap();

        for i in 0..n {
            self.reusable_ts.clear();
            for t in &self.reusable_guards {
                self.reusable_ts.push(t[i].clone());
                process_ts(&self.reusable_ts);
            }
        }

        self.reusable_guards.clear();
    }
}

pub fn main() {
    unimplemented!()
}

不与
error[E0106]: missing lifetime specifier
  --> src/main.rs:13:26
   |
13 |     reusable_guards: Vec<RwLockReadGuard<Vec<T>>>,
   |        

Playground

最佳答案

归根结底,似乎您想要做的是分配一个Vec。使用它来存储某些生命周期RwLockReadGuard<'a, Vec<T>>'a类型的元素,然后清除向量并将其放入RwLockReadGuard<'b, Vec<T>>类型的元素,其中生命周期'b是与'a不同的生命周期(实际上没有重叠),依此类推。这是行不通的,因为RwLockReadGuard<'a, Vec<T>>是与RwLockReadGuard<'b, Vec<T>>不同的类型,并且我们无法更改Vec所保存的元素的类型。

但是也许真正的目标不是用相同的Vec来保存这些不同类型的元素(这是不可能的),而是只是避免需要重新分配每个新的Vec。我们可能会问,是否可以从旧的Vec中回收已分配的内存,从而跳过不必分配下一个Vec的情况?好吧,对于一些非常丑陋,不安全的代码,可能有可能只分配一个Vec<u8>,然后在每次调用process时都要进行一些指针争夺,以将其就地转换为所需类型的Vec(大小为零,但非零)。零容量);这可能很难正确完成,并且需要根据Vecstd实现的内部细节进行操作。

也许值得退后一步,认识到每次在堆上分配内容时我们都可以问同样的问题-也就是说,是否有一种方法可以重用刚释放的内容中的空间,从而避免执行新操作分配?在某些情况下,答案可能是肯定的,但是我们不得不问一下,是否值得将我们的代码弄乱以进行优化?

这就引出了一个问题-我们是否有任何证据表明这里的分配实际上是一个重要的性能瓶颈?如果没有,也许就不用担心了。如果确实需要提高分配性能,则可以尝试使用jemalloc或某种形式的竞技场。

关于rust - 如何在结构中具有可重用的Vec <RwLockReadGuard>以避免分配?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62330718/

相关文章:

macros - 如何编写函数(或宏)来创建 Vec 和对它的引用?

rust - println 宏是否分配堆内存?

multithreading - 如何将不可变参数传递给线程? (关于终生)

c++ - 显式调用析构函数

reference - 如何为对结构的引用实现 Add 特性?

rust - 如何在 Rust 中解构和取消引用元组

memory-management - 为什么在不存储结果的情况下两次调用Box::new却分配了相同的堆地址?

rust - 为什么不能在同一结构中存储值和对该值的引用?

iterator - 我该如何编写一个迭代器来返回对自身的引用?

javascript - 在 JS 中扩展 PerformanceObserver 的范围