我想对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]
答案 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;
}