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

我们在时间要求严格的脚本中有几个地方,我们将旧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)更快 – 但是它呢?

先说几点。 一个是像这样的低级语言构造,或多或少做同样的事情几乎从来不是任何现实世界应用程序的瓶颈,所以专注于它们(通常)是愚蠢的。 其次,正如已经提到的,如果你真的关心它,你应该对它进行基准测试。 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查找仍然只有一秒,因此,除非您正在执行这些查找的度量标准,否则最好将注意力集中在其余代码上。

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

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

 $ 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) 

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

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

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

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

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

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

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

另一方面,如果从数据库中选择大量ID,则可以使用“词典”之类的内容来存储名称/值对。

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

但最终,这完全取决于您是使用不断变化的ID /字符串对还是仅使用少量常量。

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

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