有什么方法可以提高 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)));
}
基本上我必须浏览一张图片并对其应用不同的滤镜。我为此使用的基本代码如下所示,我的示例 运行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)));
}