首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何构建一个可变的Vecs池,这些Vecs在拖放时被重用?

如何构建一个可变的Vecs池,这些Vecs在拖放时被重用?
EN

Stack Overflow用户
提问于 2021-03-10 03:10:19
回答 1查看 260关注 0票数 2

我正在尝试创建一个可变的Vec对象池,这些对象可以根据需要传递给函数,并在不再需要时重用(因为我的目标是WASM,所以我不想让Vec自身重新分配和重新分配)。我有一个使用RcRefCell的实现,我想知道是否有更好的(更高效的)实现?做这个的方法。

我的当前代码使用Rc::strong_count来跟踪是否分发了缓冲区和RefCell,以允许对内部Vec的可变访问:

代码语言:javascript
复制
use std::{cell::RefCell, rc::Rc};

#[derive(Debug)]
struct BufferPool {
    buffers: Vec<Rc<RefCell<Vec<f64>>>>,
    buffer_size: usize,
}

impl BufferPool {
    fn new() -> Self {
        BufferPool {
            buffers: vec![],
            buffer_size: 3,
        }
    }
    fn add_buffer(&mut self) -> Rc<RefCell<Vec<f64>>> {
        self.buffers
            .push(Rc::new(RefCell::new(vec![0.; self.buffer_size])));
        Rc::clone(&self.buffers[self.buffers.len() - 1])
    }
    fn get_buffer(&mut self) -> Rc<RefCell<Vec<f64>>> {
        for buf in &self.buffers {
            // If the Rc count is 1, we haven't loaned the buffer out yet.
            if Rc::strong_count(&buf) == 1 {
                return Rc::clone(&buf);
            }
        }
        // If we made it here, there's no available buffer, so we need to create one.
        self.add_buffer()
    }
}

此代码可以通过以下方法进行测试:

代码语言:javascript
复制
#[test]
fn test_buffers() {
    let mut buffers = BufferPool::new();
    let buf_cell1 = buffers.get_buffer();
    {
        let mut buf1 = buf_cell1.borrow_mut();
        buf1[0] = 5.5;
    }
    {
        let buf_cell2 = buffers.get_buffer();
        let mut buf2 = buf_cell2.borrow_mut();
        buf2[1] = 6.6;
    }
    {
        let buf_cell3 = buffers.get_buffer();
        let mut buf3 = buf_cell3.borrow_mut();
        buf3[2] = 7.7;
    }
    dbg!(&buffers);
}

它提供了预期产出:

代码语言:javascript
复制
 &buffers = BufferPool {
    buffers: [
        RefCell {
            value: [
                5.5,
                0.0,
                0.0,
            ],
        },
        RefCell {
            value: [
                0.0,
                6.6,
                7.7,
            ],
        },
    ],
    buffer_size: 3,
}

但是,由于RcRefCell::borrow_mut()都在跟踪缓冲区是否已被“借出”(因为RefCell有能力出错,如果它的内容是双借出的),我所做的工作似乎有点效率低下。而且,从人体工程学的角度来看,我不能在一条线上调用buffers.get_buffer().borrow_mut()而不抱怨丢失的临时值,这是很烦人的。

所以,我的问题是:有没有更好的方法来做到这一点?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-03-14 20:21:59

正如您已经注意到的,通过Rc<RefCell<T>>提供对对象的访问是功能性的,但不太符合人体工程学。设计池的一种更好的方法是返回一个获得值所有权的包装器,然后在丢弃时将其放回池中。下面是一个如何做到这一点的“基本”示例:

代码语言:javascript
复制
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};

#[derive(Debug)]
struct BufferPool {
    buffers: RefCell<Vec<Vec<f32>>>,
    buffer_size: usize,
}

impl BufferPool {
    pub fn new() -> Self {
        BufferPool {
            buffers: RefCell::new(Vec::new()),
            buffer_size: 3,
        }
    }
    
    pub fn get_buffer(&self) -> BufferPoolRef {
        let mut buffers = self.buffers.borrow_mut();
        let buffer = buffers.pop().unwrap_or_else(|| vec![0.0; self.buffer_size]);
        BufferPoolRef { pool: self, buffer }
    }
    
    fn return_buffer(&self, buffer: Vec<f32>) {
        let mut buffers = self.buffers.borrow_mut();
        buffers.push(buffer);
    }
}

struct BufferPoolRef<'a> {
    pool: &'a BufferPool,
    buffer: Vec<f32>,
}

impl Deref for BufferPoolRef<'_> {
    type Target = Vec<f32>;
    
    fn deref(&self) -> &Self::Target {
        &self.buffer
    }
}

impl DerefMut for BufferPoolRef<'_> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.buffer
    }
}

impl Drop for BufferPoolRef<'_> {
    fn drop(&mut self) {
        let buffer = std::mem::take(&mut self.buffer);
        self.pool.return_buffer(buffer);
    }
}

fn main() {
    let mut buffers = BufferPool::new();
    let mut buf1 = buffers.get_buffer();
    {
        buf1[0] = 5.5;
    }
    {
        let mut buf2 = buffers.get_buffer();
        buf2[1] = 6.6;
    }
    {
        let mut buf3 = buffers.get_buffer();
        buf3[2] = 7.7;
    }
    drop(buf1);
    dbg!(&buffers);
}
代码语言:javascript
复制
[src/main.rs:71] &buffers = BufferPool {
    buffers: RefCell {
        value: [
            [
                0.0,
                6.6,
                7.7,
            ],
            [
                5.5,
                0.0,
                0.0,
            ],
        ],
    },
    buffer_size: 3,
}

但是,您可以使用像对象池救生员这样的机箱,而不是自己动手。它们都在WASM上工作,并使用我前面描述的机制。下面是一个基于对象池的BufferPool实现:

代码语言:javascript
复制
use object_pool::{Pool, Reusable};

struct BufferPool {
    pool: Pool<Vec<f32>>,
    buffer_size: usize,
}

impl BufferPool {
    pub fn new() -> Self {
        BufferPool {
            pool: Pool::new(2, || vec![0.0; 3]),
            buffer_size: 3,
        }
    }
    
    pub fn get_buffer(&self) -> Reusable<Vec<f32>> {
        self.pool.pull(|| vec![0.0; self.buffer_size])
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66557805

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档