【问题标题】:Why are iterators in Rust seemingly very slow when I iterate over them?为什么当我迭代它们时,Rust 中的迭代器看起来很慢?
【发布时间】:2020-11-07 12:10:29
【问题描述】:

我有一个使用 mmap 读取的大文件。我想在每一行上做一些操作,所以我在上面调用 split() ,这给了我每一行的迭代器:

let file = File::open("myfile").unwrap();
let mmap = unsafe { MmapOptions::new().map(&file).unwrap() };
//splitting by newline
let iter = mmap.split(|elem| elem == &b'\n');

这很好用,不会给我带来任何问题 - 运行速度非常快。

但是,当我通过迭代器时,它会跳转并且通过 for 循环大约是读取和拆分时间的 4 倍。

另外,这是不处理该行或在 for 循环内做任何事情:

for elem in iter {
  //process the line
}

由于性能是一个问题 - 我觉得它能够非常快速地读取和拆分文件很奇怪,但是,当我通过迭代器时它变得非常慢。我错过了什么吗?我对生锈的了解也很有限,所以不确定我是否在做坏事。有什么可以帮助我优化这一点并让我更快的访问时间吗?

此外,并行迭代器在我的情况下并没有多大帮助 - 它们增加的开销是不值得的。

整个文件:

use memmap::MmapOptions;
use std::fs::File;
use std::time::{Duration, Instant};

fn main() {

    let now = Instant::now();
    let file = File::open("myfile").unwrap();
    let mmap = unsafe { MmapOptions::new().map(&file).unwrap() };
    let iter = mmap.split(|elem| elem == &b'\n');

    /*
    for elem in iter {
      //do nothing
    }
    */
    println!("{:?}", now.elapsed());
}

如果我取消注释 for 循环,它会变慢 4 倍。我正在使用 --release 标签构建,所以这不是问题。

【问题讨论】:

  • 迭代器根本不做任何事情,除非它们被迭代。您在快速情况下测量什么?
  • Cerberus 是正确的。 .split() 只是构造一个 Split 结构。迭代器是惰性的,在你真正使用它们之前什么都不做,例如在 for 循环中。

标签: rust iterator mmap


【解决方案1】:

代码只在取消注释 for 循环时看起来很慢,因为 否则它不会做任何事情迭代器是惰性的,并且只在消耗时执行一些活动。

引用Rust Programming language, chapter 13, section 2中的相关部分:

在 Rust 中,迭代器是 惰性的,这意味着它们只有在您调用消耗迭代器的方法来使用它时才会起作用。 [...] 在迭代器上调用next 方法会更改迭代器用来跟踪它在序列中的位置的内部状态。换句话说,这段代码消耗,或用完迭代器

for 循环是使用迭代器的构造示例。在内存映射数据上调用.split() 只会为该迭代器创建一个适配器(请注意,这并不意味着它会创建多个迭代器)。适配器是使用迭代器的一种常用方式,正如本书中所描述的那样。

Iterator trait 上定义的其他方法,称为迭代器适配器,允许您将迭代器更改为不同类型的迭代器。您可以将多个调用链接到迭代器适配器,以一种可读的方式执行复杂的操作。但是因为所有迭代器都是惰性的,所以您必须调用其中一个使用适配器的方法才能从对迭代器适配器的调用中获取结果。

因此,该示例不会急切地在内存中创建这些拆分,并且该程序仅在存在 for 循环或以其他方式消耗迭代器时才做一些有价值的事情。

另见:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-12
    • 2011-05-07
    • 1970-01-01
    • 2016-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-16
    相关资源
    最近更新 更多