是否有一些方法可以提高Rust中传递闭包的性能?



我基本上要浏览一张图片并对其应用不同的滤镜。我使用的基本代码看起来像这样,在我的例子中运行大约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;
}
}
}

——编辑:以下是如何将迭代器传递给捕获的简化版本:

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)));
}

最新更新