生成字符串和识别子字符串非常慢

时间:2015-05-16 06:57:02

标签: string substring rust

我想对Rust中的某些操作进行基准测试,但我似乎遇到了一些问题:

fn main(){

    let needle   = (0..100).map(|_| "b").collect::<String>();
    let haystack = (0..100_000).map(|_| "a").collect::<String>();

    println!("Data ready.");

    for _ in 0..1_000_000 {
        if haystack.contains( &needle ) {
            // Stuff...
        }
    }

}

上面需要很长时间才能完成,而Ruby中的相同操作在大约4.5秒内完成:

needle   = 'b' * 100
haystack = 'a' * 100_000

puts 'Data ready.'

1_000_000.times do
    haystack.include? needle
end

我不禁想到我做了一些根本错误的事情。 在Rust中这样做的正确方法是什么?

rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14)
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]

1 个答案:

答案 0 :(得分:6)

今天合并了此问题的修复程序。这意味着它应该是下一个夜晚的一部分,并且预计将在Rust 1.3中发布。该修复程序恢复了Rust曾经拥有的Two-way substring search实现,并将其调整为标准库中的新Pattern API

双向算法非常适合Rust的libcore,因为它是一个使用O(1)空间且不需要动态分配的线性时间子串搜索算法。

特定的实现包含一个简单的添加,它会极快地拒绝这个问题中的特定查询(不,它不是因为这个问题而编写的,它也是旧代码的一部分)。

在设置过程中,搜索器会为指针计算一种指纹:对于指针中的每个字节,取其低6位,即0-63,然后设置u64变量中的相应位byteset

let byteset = needle.iter().fold(0, |a, &b| (1 << ((b & 0x3f) as usize)) | a);

由于针只包含&#39; b,所以byteset的值只有第34位(98 & 63 == 34)。

现在我们可以测试任何字节是否可能是指针的一部分。如果在byteset中没有设置相应的位,则指针无法匹配。在这种情况下,我们在大海捞针中测试的每个字节都是&#39; a&#39; (97 & 63 == 33),它无法匹配。因此算法将读取单个字节,拒绝它,然后跳过针的长度。

fn byteset_contains(&self, byte: u8) -> bool {
    (self.byteset >> ((byte & 0x3f) as usize)) & 1 != 0
}

// Quickly skip by large portions unrelated to our substring
if !self.byteset_contains(haystack[self.position + needle.len() - 1]) {
    self.position += needle.len();
    continue 'search;
}

From libcore/str/pattern.rs in rust-lang/rust

相关问题