在哈希查找中,符号如何比字符串更快?

时间:2017-07-16 17:59:47

标签: ruby hash hashmap symbols

我理解为什么使用符号而不是哈希中的字符串的一个方面。即,在内存中只有一个给定Symbol的实例,而给定String的多个实例可能具有相同的值。

我不明白的是,在哈希查找中,符号比字符串更快。我已经查看了答案here,但我仍然没有完全理解答案。

如果:foo.hash == :foo.object_id返回true,那么它会有所帮助,因为它可以使用对象ID作为哈希的值并且不会#39} ;每次都必须计算它。但情况并非如此,:foo.object_id不等于:foo.hash。因此我的困惑。

3 个答案:

答案 0 :(得分:3)

hash没有义务等同于object_id。这两件事完全不同。 hash的要点是尽可能确定且随机,以便您插入哈希值的值均匀分布。 object_id的要点是定义唯一的对象标识符,但不要求它们是随机的或均匀分布的。事实上,随机化它们会适得其反,只会让事情变得更慢。

符号往往更快的原因是因为它们的内存被分配一次(除了垃圾收集问题)并且为同一符号的所有实例回收。字符串不是那样的。它们可以以多种方式构造,甚至两个逐字节相同的字符串也可能是不同的对象。事实上,除非你确定他们是同一个对象,否则假定他们比其他人更安全。

现在谈到计算hash时,即使字符串变化很小,该值也必须随机变化。由于符号无法改变计算,因此可以进行更多优化。你可以计算object_id的哈希,因为它不会发生变化,例如,当字符串需要考虑自身的内容时,这可能是动态的。

尝试基准测试:

require 'benchmark'

count = 100000000

Benchmark.bm do |bm|
  bm.report('Symbol:') do
    count.times { :symbol.hash }
  end
  bm.report('String:') do
    count.times { "string".hash }
  end
end

这给了我这样的结果:

       user     system      total        real
Symbol:  6.340000   0.020000   6.360000 (  6.420563)
String: 11.380000   0.040000  11.420000 ( 11.454172)

在这个最微不足道的情况下,它容易快2倍。基于一些基本测试,当字符串变长但符号时间保持不变时,字符串代码的性能会降低 O(N)

答案 1 :(得分:1)

只想补充一点,我并不完全同意@tadman提出的数字。在我的测试中,使用计算'#hash'最多可以快1.5。我使用benchmark/ips来测试性能。

require 'benchmark/ips'

Benchmark.ips do |bm|
  bm.compare!
  bm.report('Symbol:') do
    :symbol.hash
  end
  bm.report('String:') do
    'string'.hash
  end
end

这导致

Comparison:
             Symbol:: 10741305.8 i/s
             String::  7051559.3 i/s - 1.52x slower

此外,如果启用“冻结字符串文字”(在将来的ruby版本中将是默认值),差异将降至因子1.2:

# frozen_string_literal: true

Comparison:
             Symbol::  9014176.3 i/s
             String::  7532196.9 i/s - 1.20x slower

答案 2 :(得分:0)

作为散列键的字符串的额外开销是因为字符串是可变的,并且通常也使用散列键,所以Hash类复制所有字符串键(可能使用像dup或clone这样的方法)以保护来自密钥损坏的哈希的完整性。

考虑:

irb(main):001:0> a = {}
=> {}
irb(main):002:0> b = "fred"
=> "fred"
irb(main):003:0> a[b] = 42
=> 42
irb(main):004:0> a
=> {"fred"=>42}
irb(main):005:0> b << " flintstone"
=> "fred flintstone"
irb(main):006:0> a
=> {"fred"=>42}
irb(main):007:0> b
=> "fred flintstone"
irb(main):008:0>
irb(main):008:0> b.object_id
=> 17350536
irb(main):009:0> a.keys[0].object_id
=> 15113052
irb(main):010:0>

符号是不可改变的,不需要这么激烈的措施。