为什么 BufReader.read_line() 比 stdin().read_line() 慢?

0 io rust

最近,我正在学习如何在 Rust 中更快地处理 I/O。我在标准文档中找到了“BufReader”和“BufWriter”,它说这些读取器和写入器使频繁的读取和写入速度更快,因为它有内部缓冲区。

因此,我尝试从由换行符分隔的文件中读取 1,000,000 个整数数据,并将该数据写入标准输出。

使用BufWriter写入 1,000,000 条数据比仅使用 writeln 快得多!宏观如我所料。但是,就BufReader而言, stdin().read_line() 比 BufReader.read_line() 快得多。

这是我的代码。

use std::fmt::Write as FmtWrite;
use std::io::{self, BufRead, BufReader, BufWriter, Read, Write};
use std::time::SystemTime;

fn main() {
    let now = SystemTime::now();

    // 324 millisec
    // let mut input = String::new();
    // io::stdin().read_line(&mut input).unwrap();
    // let n = input.trim().parse::<usize>().unwrap(); 
    //
    // for _ in 0..n {
    //     input.clear();
    //     io::stdin().read_line(&mut input).unwrap();
    //     let num = input.trim().parse::<usize>().unwrap();
    // }

    // 484 millisec???
    let mut input = String::new();
    let mut reader = BufReader::new(io::stdin().lock());
    reader.read_line(&mut input).unwrap();
    let n = input.trim().parse::<usize>().unwrap();

    for _ in 0..n {
        input.clear();
        reader.read_line(&mut input).unwrap();
        let num = input.trim().parse::<usize>().unwrap();
    }

    println!("{}", now.elapsed().unwrap().as_millis());
}
Run Code Online (Sandbox Code Playgroud)

这是我的 input.txt 的样子。

1000000
1
35
620
342
5
... and more ...
Run Code Online (Sandbox Code Playgroud)

正如评论所说,使用 stdin().read_line() 读取大约需要 300 毫秒,但 BufReader.read_line() 从文件中读取 1,000,000 个数据花费了 400 毫秒以上。

为什么 BufReader 速度较慢?我读到 stdin().read_line() 每次调用时都会锁定和解锁 stdin。但是 BufReader 在创建时只锁定 stdin 一次。所以,从逻辑上讲,BufReader 应该更快,对吗?而且它还有缓冲!

我是否误解了BufReader的使用?

任何评论都会有帮助。

sk_*_*ant 5

文档页面Stdin

每个句柄都是对此进程的输入数据的全局缓冲区的共享引用。可以使用句柄lock来获得对BufRead方法的完全访问权限(例如,.lines())。否则,对该句柄的读取将相对于其他读取被锁定。

换句话说,Stdin已经缓冲了,再次缓冲只会增加开销。话虽这么说,最有效的解决方案可能是直接锁定Stdin并使用StdinLock

use std::io::{self, BufRead};

fn main() {

    let mut input = String::new();
    let mut stdin = io::stdin().lock();
    
    stdin.read_line(&mut input).unwrap();
    let n = input.trim().parse::<usize>().unwrap(); 
    
    for _ in 0..n {
     input.clear();
     io::stdin().read_line(&mut input).unwrap();
     let num = input.trim().parse::<usize>().unwrap();
    }

}
Run Code Online (Sandbox Code Playgroud)

铁锈游乐场