Indexing 更有效的方法来遍历展平二维向量的列

Indexing 更有效的方法来遍历展平二维向量的列,indexing,rust,iterator,Indexing,Rust,Iterator,所以我有一个Vec存储在self.board.data中,它代表一个二维网格。逐行遍历它并在其索引和x,y坐标之间转换很容易,但是要逐列索引它,我需要使用嵌套的for循环。有没有更有效的方法?我有类似于这个函数的函数,它会运行很多次,所以我想让它们尽可能的高效 ///returns average height of all columns fn calc_avg_height(&self) -> f32 { let mut heights = 0;

所以我有一个
Vec
存储在
self.board.data
中,它代表一个二维网格。逐行遍历它并在其索引和x,y坐标之间转换很容易,但是要逐列索引它,我需要使用嵌套的
for
循环。有没有更有效的方法?我有类似于这个函数的函数,它会运行很多次,所以我想让它们尽可能的高效

    ///returns average height of all columns
    fn calc_avg_height(&self) -> f32 {
        let mut heights = 0;
        for x in 0..self.board.width {
            for y in 0..self.board.height {
                if self.board.data[(y*self.board.width)+x] {
                    heights+=self.board.height-y;
                    break
                }
            }
        }
        heights as f32/self.board.width as f32
    }

您可以预加载索引,这样您只需要对它们进行迭代。在时间上效率更高,但在空间上效率不高:

fn预加载索引(行大小:usize,列大小:usize)->Vec{
让mut index=Vec::new();
对于0.行大小中的x{
对于0中的y..列大小{
设i=y*行大小+x;
指数推送(i);
}
}
索引
}
作为一个完整的例子:

使用std::fmt::Display;
结构矩阵{
行大小:usize,
列大小:usize,
资料来源:Vec,
列索引:Vec,
}
fn预加载索引(行大小:usize,列大小:usize)->Vec{
让mut index=Vec::new();
对于0.行大小中的x{
对于0中的y..列大小{
设i=y*行大小+x;
指数推送(i);
}
}
索引
}
impl矩阵{
fn新建(数据:Vec,行大小:usize,列大小:usize)->Self{
断言(data.len(),行大小*列大小);
自我{
行大小,
列的大小,
数据,
列索引:预加载索引(行大小、列大小),
}
}
fn打印行(&self){
对于self.data.iter()中的e{
println!(“{}”,e);
}
}
fn打印列(&self){
对于self.columns\u index.iter()中的i{
println!(“{}”,self.data[*i]);
}
}
}
fn main(){
让矩阵=矩阵::new(vec![1,2,3,4],2,2);
println!(“行”);
matrix.print_rows();
println!(“列”);
matrix.print_columns();
}

无论您做什么,逐列读取数据都会很慢,因为您将在内存中跳跃。与之相比,计算索引是一种噪声

有没有更有效的方法


一个想法是将位放在一起以减少8的内存。另一种方法是逐列保存数据,并仅在需要时更新(这取决于您的访问模式)。

围绕您的示例,我非常惊讶地看到这一点 索引计算(
y*width+x
)似乎不正确 由乐观主义者简化。 此外,我认为内存访问更重要 而不是索引计算中删除的乘法。 但说到时间,我可以看到一个很大的区别 (具有各种网格大小和正确/错误初始化)

编辑

在阅读了下面的一些评论之后,我比较了两个版本,
\u v1
\u v2
,在编译器资源管理器(godbolt)中。 它们看起来非常相似,只是
\u v1
使用
lea
指令和
\u v2
使用
add
inc
。 根据vtune放大器,v1的大部分时间是 花在这条明确的
lea
指令上。 所以,至少在我的电脑上,在这个具体的例子上 与直觉相反,选择
lea
而不是
add
inc
似乎比不规则的内存访问更有害 模式

/*
$rustc-C选项级别=3个进度和/进度
平均值为50.5
v1:4453毫秒
avg2=50.5
v2:2361毫秒
*/
酒吧结构板{
宽度:usize,
高度:usize,
资料来源:Vec,
}
impl板{
新店(
宽度:usize,
高度:usize,
)->自我{
自我{
宽度,
高度,
数据:vec![错误;宽度*高度],
}
}
发布fn计算平均高度v1(&self)->f32{
让mut heights=0;
对于0..self.width中的x{
对于y,在0..self.height中{
如果自数据[(y*自宽度)+x]{
高度+=自身高度-y;
打破
}
}
}
高度为f32/自宽为f32
}
酒吧fn计算平均高度v2(&self)->f32{
让mut heights=0;
对于0..self.width中的x{
设mut idx=x;
对于y,在0..self.height中{
if self.data[idx]{
高度+=自身高度-y;
打破
}
idx+=自身宽度;
}
}
高度为f32/自宽为f32
}
}
酒吧fn-run(
姓名:&str,
重复:usize,
fnct:F,
)->u128
哪里
F:Fn()->f32,
{
设mut prev=-1.0_f32;
让预热=重复/10次;
对于0.预热中的uu{
设平均值=fnct();
如果平均值!=上一个{
println!(“{}={}”,名称,平均值);
prev=平均值;
}
}
让现在=std::time::Instant::now();
对于0中的uu..重复{
设平均值=fnct();
如果平均值!=上一个{
println!(“{}={}”,名称,平均值);
prev=平均值;
}
}
now.appeased().as_millis()
}
pub fn main(){
让mut board=board::new(100100);
对于0.板高中的y{
对于0..board.width中的x{
board.data[y*board.width+x]=x==y;
}
}
让重复=1_000_000;
普林顿(
“v1:{}ms”,
运行(“avg1”,重复,| | board.calc_avg_height_v1())
);
普林顿(
“v2:{}ms”,
运行(“avg2”,重复,| | board.calc_avg_height_v2())
);
}

从缓存中读取琐碎的索引可能比计算它们慢。如果它们不在缓存中,速度肯定会慢一些。哦,谢谢!这真是太好了。我刚刚在我目前正在优化的模块中完成并实现了新的索引技术,哇,它有什么不同吗