ruby Set.include中的性能异常?带符号(2.2.2 vs 2.1.6)
在对一些代码进行基准测试以找出时,如果在检查通过include包含的元素时使用集合确实比数组快?我在集合中发现了一些关于字符串和符号的性能异常.
首先是我用于基准测试的脚本.它基本上创建一个包含50个随机50个字符串的数组,获取20个样本并检查是否包含所有样本值.相同的数据用于创建一组字符串,一组符号和一组符号. require 'benchmark/ips' require 'Set' collection_size = 50 element_length = 50 sample_size = 20 Benchmark.ips do |x| array_of_strings = begin (1..collection_size).map {|pos| (0..element_length).map { ('a'..'z').to_a[rand(26)] }.join } end array_of_symbols = array_of_strings.map(&:to_sym) set_of_strings = Set.new(array_of_strings) set_of_symbols = Set.new(array_of_symbols) sample_of_strings = array_of_strings.sample(sample_size) sample_of_symbols = array_of_symbols.sample(sample_size) x.report("array_of_strings: #{collection_size} elements with length #{element_length},sample size #{sample_of_strings.length}") { sample_of_strings.each do |s| array_of_strings.include? s end } x.report("set_of_strings: #{collection_size} elements with length #{element_length},sample size #{sample_of_strings.length}") { sample_of_strings.each do |s| set_of_strings.include? s end } x.report("array_of_symbols: #{collection_size} elements with length #{element_length},sample size #{sample_of_symbols.length}") { sample_of_symbols.each do |s| array_of_symbols.include? s end } x.report("set_of_symbols: #{collection_size} elements with length #{element_length},sample size #{sample_of_symbols.length}") { sample_of_symbols.each do |s| set_of_symbols.include? s end } x.compare! end 测试系统是2011 Macbook Pro,运行OSX 10.10.4,两个ruby版本都是使用rvm 1.26.11安装的. 使用ruby 2.2.2执行此操作时,我得到以下结果: set_of_strings: 145878.6 i/s set_of_symbols: 100100.1 i/s - 1.46x slower array_of_symbols: 81680.0 i/s - 1.79x slower array_of_strings: 43545.9 i/s - 3.35x slower 正如预期的那样,该集合比数组快,即使没有我预期的那么快.让我感到奇怪的是,包含字符串的集合比包含符号的集合更快,而对于数组,符号1更快.我多次重复脚本,比例保持不变. 为了获得更多的见解,我使用ruby 2.1.6运行了基准脚本并得到了这些结果: set_of_symbols: 202362.3 i/s set_of_strings: 145844.1 i/s - 1.39x slower array_of_symbols: 39158.1 i/s - 5.17x slower array_of_strings: 24687.8 i/s - 8.20x slower 同样的集合比数组更快,但是数组性能比ruby 2.2.2差得多,似乎性能改善与2.1.6相比非常好. 奇怪的是集合的结果.包含字符串的集合在ruby 2.2.2和2.1.6中每秒达到大约相同的指令,这应该是它应该如何.但是包含符号的集合在ruby 2.1.6中的速度是其两倍.而不是2.2.2! 我还改变了基准脚本的参数,结果保持不变.字符串集在2.1.6和2.2.2中达到大约相同的i / s,而2.2.2中的符号集要慢得多. 我现在的问题 >任何人都能解释这种行为吗? 更新1: 似乎潜在的问题是Hash类本身,它用于在Set类中存储值.刚刚创建了一个数字为1到1000的哈希作为字符串/符号键,并使用样本进行哈希[k]访问. Ruby 2.2.2: h_string: 1000 keys,sample size 200: 29374.4 i/s h_symbol: 1000 keys,sample size 200: 10604.7 i/s - 2.77x slower Ruby 2.1.6: h_symbol: 1000 keys,sample size 200: 31561.9 i/s h_string: 1000 keys,sample size 200: 25589.7 i/s - 1.23x slower 出于某种原因,在2.2.2中使用符号作为哈希键要慢得多,使用的基准测试脚本: require 'benchmark/ips' collection_size = 1000 sample_size = 200 Benchmark.ips do |x| h_string = Hash.new h_symbol = Hash.new (1..collection_size).each {|k| h_string[k.to_s] = 1} (1..collection_size).each {|k| h_symbol[k.to_s.to_sym] = 1} sample_of_string_keys = h_string.keys.sample(sample_size) sample_of_symbol_keys = sample_of_string_keys.map(&:to_sym) x.report("h_string: #{collection_size} keys,sample size #{sample_of_string_keys.length}") { sample_of_string_keys.each do |s| h_string[s] end } x.report("h_symbol: #{collection_size} keys,sample size #{sample_of_symbol_keys.length}") { sample_of_symbol_keys.each do |s| h_symbol[s] end } x.compare! end 更新2: 我用最新的ruby 2.3.0dev(2015-07-26 trunk 51391)[x86_64-darwin14]重复了这些测试,那里带有符号的集合再次快得多,并且对于一个级别的小型collection_size和sample_size,使用ruby 2.1.6 对于更大的数字,例如具有10000个值的哈希并检查100个样本,ruby 2.1.6达到了ruby-head迭代的大约两倍(并且几乎是ruby 2.2.2的3倍).因此看起来在ruby-head中正在进行一些改进,但仍然不足以恢复旧的性能. 更新3: 感谢@cremno的comment,我用2.2分支重新检查了我的初始设定基准,2.2与2.1.6处于同一水平 我现在的结论 >始终对您的实际代码进行基准测试,并考虑差异是否真的可见.在我的用例中,即使对每个Web请求进行约50次检查,总时间差仅为一毫秒的一小部分 解决方法
这是ruby代码中的一个性能问题,它修复得非常快:-)
问题:https://bugs.ruby-lang.org/issues/11396 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |