Rust在解析文件方面比Python慢。

7
我希望使用Rust来加速一些目前用Python编写的文本处理脚本。
为了测试这两种语言的性能,我决定在一个非常简单的任务上对其进行测试:
  • STDIN逐行读取文件。
  • 如果该行以>开头,则将该行保存到headers.txt文件中。
  • 否则,将该行保存到sequences.txt文件中。
对于这个测试,我使用了一个包含1000万行的fasta文件,如下所示:
$ head uniparc_active-head.fasta
>UPI0000000001 status=active
MGAAASIQTTVNTLSERISSKLEQEANASAQTKCDIEIGNFYIRQNHGCNLTVKNMCSAD
ADAQLDAVLSAATETYSGLTPEQKAYVPAMFTAALNIQTSVNTVVRDFENYVKQTCNSSA
VVDNKLKIQNVIIDECYGAPGSPTNLEFINTGSSKGNCAIKALMQLTTKATTQIAPKQVA
GTGVQFYMIVIGVIILAALFMYYAKRMLFTSTNDKIKLILANKENVHWTTYMDTFFRTSP
MVIATTDMQN
>UPI0000000002 status=active
MMTPENDEEQTSVFSATVYGDKIQGKNKRKRVIGLCIRISMVISLLSMITMSAFLIVRLN
QCMSANEAAITDAAVAVAAASSTHRKVASSTTQYDHKESCNGLYYQGSCYILHSDYQLFS
DAKANCTAESSTLPNKSDVLITWLIDYVEDTWGSDGNPITKTTSDYQDSDVSQEVRKYFC

这是我的Python脚本:

import fileinput

with open('headers.txt', 'w') as hof, \
        open('sequences.txt', 'w') as sof:
    for line in fileinput.input():
        if line[0] == '>':
            hof.write(line)
        else:
            sof.write(line)

同时,我的 Rust 脚本(我使用 cargo build --release 编译):

use std::io;
use std::fs::File;
use std::io::Write;
use std::io::BufRead;

fn main() {
    let stdin = io::stdin();
    let mut headers = File::create("headers.txt").unwrap();
    let mut sequences = File::create("sequences.txt").unwrap();

    for line in stdin.lock().lines() {
        let line = line.unwrap();
        match &line[..1] {
            ">" => writeln!(headers, "{}", line).unwrap(),
            _ => writeln!(sequences, "{}", line).unwrap(),
        }
    }
}

进行一些基准测试:

Python 2.7

$ time bash -c 'cat uniparc_active-head.fasta | python2 src/main.py'
real    0m11.704s
user    0m6.996s
sys     0m1.100s

Python 3.5

$ time bash -c 'cat uniparc_active-head.fasta | python3 src/main.py'
real    0m16.788s
user    0m12.508s
sys     0m1.576s

PyPy 5.3.1

$ time bash -c 'cat uniparc_active-head.fasta | pypy src/main.py'
real    0m6.526s
user    0m1.536s
sys     0m0.884s

Rust 1.14.0
$ cargo build --release
$ time bash -c 'cat uniparc_active-head.fasta | target/release/parse_text'
real    0m17.493s
user    0m2.728s
sys     0m15.408s

所以Rust比PyPy慢大约3倍,甚至比Python 3还要慢。

有人可以解释一下吗?是我在Rust代码中犯了错误吗?如果没有,那么处理文本文件应该使用Python/PyPy还是有其他更好的语言可用?


1
奇怪的是,就在几天前我遇到了非常相似的情况 - 我在 Julia 中重写了一个文本解析器,但原始的 Python 版本却快 3 倍。 - TigerhawkT3
9
你的 Rust 程序没有使用缓冲写入器。尝试用 io::BufWriter::new(File::create(...).unwrap()) 替换你的 File::create(...).unwrap() 调用。 - BurntSushi5
@BurntSushi5 嗯,谢谢!我感觉自己漏掉了一些基础的东西。 - ostrokach
3
我敢打赌它并不是受到输入输出限制。可能将一个 HN 评论链接过来不太好,但我最近写了一篇,并且有很多与此问题相关的细节,这些细节可能会帮助你更快地加速它!(诀窍是删除分配,甚至可能是 UTF-8 验证。) - BurntSushi5
1
有趣。我有点惊讶,但这是可能的!下一步是开始查看配置文件。我的 HN 评论和你的代码之间的重大区别在于,你正在将数据写入某个地方,而我只是在计数。那可能就是规范化器。(这意味着你最初关于 IO 是限制因素的评论是正确的!) - BurntSushi5
显示剩余3条评论
1个回答

14

如@BurntSushi5所建议的那样,替换

let mut headers = File::create("headers.txt").unwrap();
let mut sequences = File::create("sequences.txt").unwrap();

随着

let mut headers = io::BufWriter::new(File::create("headers.txt").unwrap());
let mut sequences = io::BufWriter::new(File::create("sequences.txt").unwrap());

速度提升到了我的预期:

$ time bash -c 'cat uniparc_active-head.fasta | target/release/parse_text'
real    0m5.645s
user    0m1.396s
sys     0m0.804s

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接