有什么方法可以提高 Rust 中传递闭包的性能吗?

Is there some way to improve the performance of passing closures in Rust?

基本上我必须浏览一张图片并对其应用不同的滤镜。我为此使用的基本代码如下所示,我的示例 运行s 在 ~0.37 秒内:

    let mut changed = true;
    while changed {
        changed = false;
        // I need to clone here so I dont use the changed data
        let past = self.grid.clone();
        // My data is in a vector, this gives me all x/y combinations
        for (x, y) in GridIndexIter::from(0, self.width as i32, 0, self.height as i32) {
            // This gives me the index of the current piece
            let i = self.ind(x, y);
            let root = past[i];
            // Here I apply my filter (if no value, set it to a neighboring value)
            if root.elevation == None {
                let surround: Option<Tile> = GridIndexIter::from(-1, 2, -1, 2)
                    .map(|(x_add, y_add)| past[self.ind(x + x_add, y + y_add)])
                    .filter(|tile| tile.elevation != None)
                    .nth(0);
                if let Some(tile) = surround {
                    self.grid[i].elevation = tile.elevation;
                    changed = true;
                }
            }
        }
    }

但是因为我想 运行 多个过滤器,所有这些过滤器都以相同的方式应用但在实际计算中有所不同(我可能想要平滑值等等),所以我尝试将其拆分进入应用过滤器的基本逻辑和该过滤器的逻辑,我将其设置为闭包,但现在我的示例需要大约 4.5 秒:

fn apply_filter(&mut self, condition: fn(Tile) -> bool, filter: fn(Vec<Tile>) -> Option<Tile>) {
    let mut changed = true;
    while changed {
        changed = false;
        let past = self.grid.clone();
        for (x, y) in GridIndexIter::from(0, self.width as i32, 0, self.height as i32) {
            let i = self.ind(x, y);
            let root = past[i];
            if condition(root) {
                if let Some(tile) = filter(GridIndexIter::from(-1, 2, -1, 2)
                        .map(|(x_add, y_add)| past[self.ind(x + x_add, y + y_add)])
                        .collect()) {
                    self.grid[i].elevation = tile.elevation;
                    changed = true;
                }
            }
        }
    }
}

self.apply_filter(|tile| tile.elevation == None,
        |past| {
            if let Some(tile) = past.iter().filter(|tile| tile.elevation != None).nth(0) {
                return Some(Tile {
                    elevation: tile.elevation,
                    ..past[4]
                });
            } None
        }
    );

我弄错了吗?有什么方法可以使关闭更有效率吗?还有其他方法可以达到同样的目的吗?

由于很难重现您的代码,我只是想建议一个更清晰的版本,也许更容易调试。显然这都是未经测试的,可能无法编译

loop {
    let past = self.grid.clone();
    let updates_count = GridIndexIter::from(0, self.width as i32, 0, self.height as i32)
        .enumerate()
        .filter(|(i,_)| &past[i].elevation.is_none()) // captures &past
        .filter_map(|(i,(x,y))| 
            GridIndexIter::from(-1, 2, -1, 2) // captures &self, &past
                .map(|(x_add, y_add)| &past[self.ind(x + x_add, y + y_add)])
                .filter(|tile| tile.elevation.is_some())
                .nth(0)
                .map(|tile| (i,tile))) // either Some((i,tile)) or None
        .map(|(i,tile)| self.grid[i].elevation = tile.elevation)
        .count();
    if updates_count == 0 {
        break;
    }
}

带有闭包的版本可能看起来像那样(注意 filter 是如何定义的,它接受迭代器,而不是向量)

fn apply_filter(&mut self, condition: fn(Tile) -> bool, filter: fn(impl Iterator<Item=Tile>) -> Option<Tile>) {
    loop {
        let past = self.grid.clone();
        let updates_count = GridIndexIter::from(0, self.width as i32, 0, self.height as i32)
            .enumerate()
            .filter(|(i,_)| condition(&past[i])) // captures &past
            .filter_map(|(i,(x,y))| 
                filter(GridIndexIter::from(-1, 2, -1, 2) // captures &self, &past
                    .map(|(x_add, y_add)| &past[self.ind(x + x_add, y + y_add)]))
                .map(|tile| (i,tile))) // either Some((i,tile)) or None
            .map(|(i,tile)| self.grid[i].elevation = tile.elevation)
            .count();
        if updates_count == 0 {
            break;
        }
    }
}

-- 编辑: 以下是如何将迭代器传递给捕获的简化版本:Playground of the following code

use std::ops::Fn;

#[derive(PartialEq, Debug)]
struct Tile(i32);

type TilesIter<'a> = &'a mut dyn Iterator<Item=Tile>;

fn apply_function<'a>(func: &'a dyn Fn(TilesIter) -> Option<Tile>) -> Option<Tile> {
    let tiles = vec![Tile(0), Tile(1), Tile(2), Tile(3)];
    func(&mut tiles.into_iter())
}


fn main() {
    let result_tile = apply_function(&|iter: TilesIter| iter.last());
    println!("{:?}", result_tile); // Some(Tile(3))
    assert_eq!(result_tile, Some(Tile(3)));
}