哪个在ruby中更快 - 哈希查找或带有case语句的函数?

时间:2010-11-14 15:50:12

标签: ruby performance

我们在时间要求严格的脚本中有一些地方可以将旧ID转换为字符串。目前,我们在函数中使用case语句,如下所示:

def get_name id
  case id
    when 1
      "one thing"
    when 3
      "other thing"
    else
      "default thing"
  end
end

我正在考虑用哈希查找替换它,如下所示:

NAMES = {
  1 => "one thing",
  3 => "other thing",
}
NAMES.default = "default thing"

使用NAMES[id]而不是get_name(id)感觉应该更快 - 但它是吗?

7 个答案:

答案 0 :(得分:30)

首先是几点。一个是像这样的低级语言构造,或多或少做同样的事情几乎从来不是任何现实世界应用程序的瓶颈,所以专注于它们(通常)是愚蠢的。其次,正如已经提到的,如果你真的关心它,你应该对它进行基准测试。 Ruby的基准测试和配置文件工具肯定不是编程生态系统中最先进的,但他们完成了工作。

我的直觉是哈希会更快,因为(再次,我猜)case语句必须依次检查每个条件(找到项目O(n)而不是O(1))。但是,让我们检查一下!

完整基准测试代码位于https://gist.github.com/25基本上,它会生成一个文件,用于定义相应的case / hash,然后使用它们。我继续将哈希查找放在方法调用中,这样开销就不会成为一个因素,但在现实生活中,没有理由将它放在方法中。

这是我得到的。在每种情况下,我都在进行10,000次查找。时间是以秒为单位的用户时间

Case statement, 10 items  0.020000
Hash lookup, 10 items     0.010000

Case statement, 100 items  0.100000
Hash lookup, 100 items     0.010000

Case statement, 1000 items  0.990000
Hash lookup, 1000 items     0.010000

所以,看起来非常像case语句是O(n)(那里没有震撼)。另请注意,即使在case语句中,10K查找仍然只有一秒,因此,除非您正在执行这些查找的度量标准,否则最好专注于其余代码。

答案 1 :(得分:7)

由于这取决于许多因素(您想要转换多少个不同的ID,编译器如何智能地编译case when statemens),我的建议是:测量它

编写一个小的测试例程,然后将10.000.000 ids转换为字符串。在任一实现中执行此操作几次并比较结果。如果你没有显着差异,可以采取你喜欢的任何东西(我认为,哈希解决方案更优雅......)

答案 2 :(得分:1)

$ ruby -v
ruby 1.9.2p0 (2010-08-18 revision 29036) [i686-linux]

$ cat hash_vs_case.rb 
require 'benchmark'

def get_from_case(id)
  case id
    when 1
      "one thing"
    when 3
      "other thing"
    else
      "default thing"
  end
end

NAMES = {
  1 => "one thing",
  3 => "other thing",
}
NAMES.default = "default thing"

def get_from_hash(arg)
  NAMES[arg]
end

n = 1000000
Benchmark.bm do |test|
  test.report("case  1") { n.times do; get_from_case(1); end }
  test.report("hash  1") { n.times do; get_from_hash(1); end}
  test.report("case 42") { n.times do; get_from_case(42); end }
  test.report("hash 42") { n.times do; get_from_hash(42); end}
end

$ ruby -w hash_vs_case.rb 
      user     system      total        real
case  1  0.330000   0.000000   0.330000 (  0.422209)
hash  1  0.220000   0.000000   0.220000 (  0.271300)
case 42  0.310000   0.000000   0.310000 (  0.390295)
hash 42  0.320000   0.010000   0.330000 (  0.402647)

这就是你想要升级的原因:

$ ruby -v
ruby 1.8.7 (2009-06-12 patchlevel 174) [i486-linux]

$ ruby -w hash_vs_case.rb 
      user     system      total        real
case  1  1.380000   0.870000   2.250000 (  2.738493)
hash  1  1.320000   0.850000   2.170000 (  2.642013)
case 42  1.500000   0.960000   2.460000 (  3.029923)
hash 42  1.890000   0.890000   2.780000 (  3.456969)

答案 3 :(得分:1)

这是一个示例,可以更快地显示符号查找的情况。前面的示例是基于整数的键。

https://gist.github.com/02c8f8ca0cd4c9d9ceb2

答案 4 :(得分:0)

为什么不在代码的时间关键部分使用case语句内联,而不是将其作为自己的函数调用?这避免了调用堆栈的开销,并且还避免了散列查找的开销。

您也可以自己做基准测试。做这样的事情:

t = Time.now
1_000_000.times { ...your code... }
secs = Time.now - t

用每个选项替换“您的代码”。

答案 5 :(得分:0)

正如马丁所说,这取决于你想要检查的ID数量。您是从数据库中选择ID和相应的字符串,还是要对其进行硬编码。如果只有几张支票,那么你可以选择CASE。但如果需要,没有选项可以修改ID / String对。

另一方面,如果您从数据库中挑选大量ID,则可以使用类似Dictionary的内容来存储名称/值对。

虽然字典在内存中,但查找速度可能很快。

但最终,这一切都取决于你是在使用不断变化的ID /字符串对还是仅使用少量常量。

答案 6 :(得分:-1)

case when n 的复杂性 hash lookup具有 ln(n)复杂性,但使用其他对象(哈希)并调用其方法有其自身的惩罚。

因此,如果你有很多案例(数千,数百万......)哈希查找更好。但在你的情况下,当你只有3个变种时,case when当然会快得多。