【问题标题】:Generating strings and identifying substrings is very slow生成字符串和识别子字符串非常慢
【发布时间】:2015-07-28 03:50:38
【问题描述】:

我想对 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]

【问题讨论】:

  • 对于那些想知道的人:在这种情况下,这不是优化问题。即使开启了优化,rust 版本的运行时间仍然很长。
  • 认为可能是 contains for strings 采取了额外的措施来正确处理 UTF-8,但是,当我去检查 contains for 的速度时常规切片我发现 Rust 甚至没有一个 o_O。我幼稚的实现仍然非常缓慢。
  • 自己试过了; Ruby 用了 5 秒,Rust 用了 7 分钟。快速了解一下 Rust 的实现(请参阅 libcore/str/pattern.rs)让 Rust 的搜索器看起来完全天真。如果 Ruby 实现在做任何事情,甚至有点聪明,那么 Rust 慢得多也就不足为奇了。无论哪种方式,看起来都值得提出性能问题。
  • 顺便说一句,python 版本也和 Ruby 一样快。

标签: string substring rust


【解决方案1】:

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

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

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

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

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

由于 needle 仅包含 'b',因此 byteset 的值将仅设置第 34 位 (98 &amp; 63 == 34)。

现在我们可以测试任何字节是否可能是针的一部分。如果byteset 中没有设置其对应位,则针无法匹配。在这种情况下,我们在 haystack 中测试的每个字节都是 'a' (97 &amp; 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

【讨论】:

  • 与 Ruby 相比,如果 needle 和 haystack 都是随机数据,Rust 的性能会如何?在这种情况下,Rust 的整体设计有什么好处吗?
  • 我认为两者都应该比“aa..”查询中的“bb..”慢得多。我认为他们处于公平的竞争环境中。请注意,Rust 中的新字符串搜索仍然是用边界检查安全的 Rust 编写的,无论如何它似乎很有竞争力。
  • 我很感兴趣,如果您使用这个新的 impl 对 rust 和 ruby​​ 进行新的性能比较。
  • 慢得多我的意思是在我的笔记本电脑上,“aa..”中的“bb..”查询将在每秒拒绝 30 GB 的干草堆(因为它只读取一小部分字节),而更正常的字符串搜索将以每秒 300 MB 的 haystack 为基准。
猜你喜欢
  • 2014-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-25
  • 2014-03-11
  • 1970-01-01
  • 2018-03-14
  • 2020-12-04
相关资源
最近更新 更多