XML文件上的Regex :: captures_iter比预期的要慢

use*_*375 2 regex rust

我的第一个轻微的Rust项目之一涉及在大型XML文件上运行正则表达式:

extern crate regex;

use regex::Regex;
use std::fs::File;
use std::io::Read;

fn main() {
    let filename = "data.xml";
    let mut f = File::open(filename).expect("file not found");

    let mut contents = String::new();
    f.read_to_string(&mut contents)
        .expect("something went wrong reading the file");

    let re = Regex::new("url=\"(?P<url>.+?)\"").unwrap();
    let urls: Vec<&str> = re.captures_iter(&contents)
        .map(|c| c.name("url").unwrap().as_str())
        .collect();

    println!("{}", urls.len());
}
Run Code Online (Sandbox Code Playgroud)

我确信我做的事情非常低效:

time ./target/release/hello_cargo 144408 ./target/release/hello_cargo
1.60s user
0.03s system
99% cpu
1.643 total 
Run Code Online (Sandbox Code Playgroud)

99%的CPU使用率是由系统引起的,这似乎是不寻常的.

Python 2.7在不到一秒的时间内完成同样的工作:

import re 
data = open('data.xml').read()
urls = set(re.findall('url="(.+?)"', data))
print len(urls)
Run Code Online (Sandbox Code Playgroud)

使用BufReader这样似乎不会改变性能:

let f = File::open(filename).expect("file not found");
let mut reader = BufReader::new(f);
let mut contents = String::new();
reader
    .read_to_string(&mut contents)
    .expect("something went wrong reading the file");
Run Code Online (Sandbox Code Playgroud)

如果您想在本地尝试,这是压缩的XML文件.

我做得怎么样?

Bur*_*hi5 6

您的问题的答案是,您编写的Rust代码在正则表达式的标准用法方面是最佳的.之所以它比Python慢​​一点(在我的机器上慢了3倍)是因为解析捕获组在Rust的正则表达式箱中没有得到太多的优化关注.特别是,解析捕获组需要运行较慢的内部正则表达式引擎.

在今天提出的问题中,请注意您的Python程序没有做同等的工作,因为它所做的只是收集匹配并计算它们.Rust程序实际上是提取一个捕获组,这是更多的工作.例如,如果您使用此代码:

let urls: Vec<&str> = re.find_iter(&contents).map(|m| m.as_str()).collect();
Run Code Online (Sandbox Code Playgroud)

那么Rust程序正在做与Python程序相同的工作,它在我的机器上快了约2倍.现在,公平地说,如果您修改Python程序以执行与原始Rust程序相同的工作,即,

urls = set(m.group('url') for m in re.finditer('url="(?P<url>.+?)"', data))
Run Code Online (Sandbox Code Playgroud)

那么Python程序只会慢一些,原来的Rust程序仍然会明显变慢,如上所述.

如果您没有等待正则表达式箱更好地优化捕获处理,那么您希望今天让程序运行得更快,然后您可以利用正则表达式的特定功能.也就是说,避免要求捕获组,只是直接从匹配的文本中提取URL.像这样:

let urls: Vec<&str> = re
    .find_iter(&contents)
    .map(|m| {
        let text = m.as_str();
        &text[5..text.len() - 1]
    })
    .collect();
Run Code Online (Sandbox Code Playgroud)

这与我上面的修改一样快,比Python快2倍.不理想,但它是一些东西.