解析和搜索字符串的更好方法是什么?

时间:2015-08-13 15:14:15

标签: python rust

我一直在寻找加速基本的Python函数,基本上只需要一行文本并检查子行的行。 Python程序如下:

import time

def fun(line):
    l = line.split(" ", 10)
    if 'TTAGGG' in l[9]:
        pass  # Do nothing

line = "FCC2CCMACXX:4:1105:10758:14389# 81 chrM 1 32 10S90M = 16151 16062 CATCACGATGGATCACAGGTCTATCACCCTATTAACCACTCACGGGAGCTTTCCATGCATTTGGTATTTTCGTCTGGGGGGTGTGCACGCTTAGGGGATAGCATTG bbb^Wcbbbbccbbbcbccbba]WQG^bbcdcb_^_c_^`ccdddeeeeeffggggiiiiihiiiiihiiihihiiiihghhiihgfgfgeeeeebbb NM:i:1 AS:i:85 XS:i:65 RG:Z:1_DB31"

time0 = time.time()
for i in range(10000):
    fun(line)
print time.time() - time0

我想看看我是否可以使用Rust的一些高级功能来获得一些性能,但代码运行速度要慢得多。 Rust转换是:

extern crate regex;
extern crate time;
use regex::Regex;

fn main() {
    let line = "FCC2CCMACXX:4:1105:10758:14389# 81 chrM 1 32 10S90M = 16151 16062 CATCACGATGGATCACAGGTCTATCACCCTATTAACCACTCACGGGAGCTTTCCATGCATTTGGTATTTTCGTCTGGGGGGTGTGCACGCTTAGGGGATAGCATTG bbb^Wcbbbbccbbbcbccbba]WQG^bbcdcb_^_c_^`ccdddeeeeeffggggiiiiihiiiiihiiihihiiiihghhiihgfgfgeeeeebbb NM:i:1 AS:i:85 XS:i:65 RG:Z:1_DB31";    
    let substring: &str = "TTAGGG";
    let time0: f64 = time::precise_time_s();

    for _ in 0..10000 {
        fun(line, substring);
    }

    let time1: f64 = time::precise_time_s();
    let elapsed: f64 = time1 - time0;
    println!("{}", elapsed);
}


fn fun(line: &str, substring: &str) {
    let l: Vec<&str> = line.split(" ")
                .enumerate()
                .filter(|&(i, _)| i==9)
                .map(|(_, e) | e)
                .collect();

    let re = Regex::new(substring).unwrap();    
    if re.is_match(&l[0]) {
        // Do nothing
    }
}

在我的机器上,Python时间为0.0065秒vs Rusts 1.3946s。

只需检查一些基本时序,代码的line.split()部分大约需要1秒,正则表达式步长大约为0.4秒。这真的是对的吗,还是有正确计时的问题?

2 个答案:

答案 0 :(得分:8)

作为基线,我用Python 2.7.6运行你的Python程序。超过10次运行,平均时间为12.2ms,标准偏差为443μs。我不知道你是如何度过 6.5ms 的好时光。

使用Rust 1.4.0-dev(febdc3b20)运行Rust代码,没有优化,平均值为958ms,标准偏差为33ms。

使用优化(cargo run --release)运行代码,平均值为34.6ms,标准偏差为495μs。 始终在发布模式下进行基准测试

您可以进行进一步的优化:

在时序循环之外编译一次正则表达式:

fn main() {
    // ...
    let substring = "TTAGGG";
    let re = Regex::new(substring).unwrap();

    // ...

    for _ in 0..10000 {
        fun(line, &re);
    }

    // ...
}

fn fun(line: &str, re: &Regex) {
    // ...
}

平均产生10.4ms,标准偏差为678μs。

切换到子字符串匹配:

fn fun(line: &str, substring: &str) {
    // ...

    if l[0].contains(substring) {
        // Do nothing
    }
}

平均值为8.7ms,标准差为334μs。

最后,如果你只查看一个结果而不是将所有内容都收集到一个向量中:

fn fun(line: &str, substring: &str) {
    let col = line.split(" ").nth(9);

    if col.map(|c| c.contains(substring)).unwrap_or(false) {
        // Do nothing
    }
}

平均值为6.30ms,标准差为114μs。

答案 1 :(得分:3)

Python的直接翻译将是

extern crate time;

fn fun(line: &str) {
    let mut l = line.split(" ");
    if l.nth(9).unwrap().contains("TTAGGG") {
        // do nothing
    }
}

fn main() {
    let line = "FCC2CCMACXX:4:1105:10758:14389# 81 chrM 1 32 10S90M = 16151 16062 CATCACGATGGATCACAGGTCTATCACCCTATTAACCACTCACGGGAGCTTTCCATGCATTTGGTATTTTCGTCTGGGGGGTGTGCACGCTTAGGGGATAGCATTG bbb^Wcbbbbccbbbcbccbba]WQG^bbcdcb_^_c_^`ccdddeeeeeffggggiiiiihiiiiihiiihihiiiihghhiihgfgfgeeeeebbb NM:i:1 AS:i:85 XS:i:65 RG:Z:1_DB31";

    let time0 = time::precise_time_s();
    for _ in 0..10000 {
        fun(line);
    }
    println!("{}", time::precise_time_s() - time0);
}

在稳定版(1.2.0)上使用cargo run --release,与Python的0.0267相比,我得到0.0240(CPython,2.7.10)。鉴于Python在字符串上的in只是一个C例程,这是合理的。

令人印象深刻的是,在测试版(1.3.0)和夜间测试版(1.4.0)上,这种情况降低到大约0.0122,或者是CPython速度的两倍左右!