首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Ruby枚举器性能问题

Ruby枚举器性能问题
EN

Stack Overflow用户
提问于 2013-02-21 03:42:11
回答 2查看 331关注 0票数 2

基于我之前的相关question,我发现在使用Enumerator类生成无限序列时存在巨大的性能差距。在此之前,我确实认为故障出在Enumerable方法、takedrop上,但以下基准测试没有证实这一说法。

创建自然数生成器并按区间显示数字的示例(10e7-10,10e7>:

代码语言:javascript
复制
require 'benchmark'

nats_range = (1..Float::INFINITY)
nats_enum = Enumerator.new {|y| i=1; loop { y << i; i+=1 }}

puts "#{'_'*79+"\n"}Benchmarking Enumerable methods on Range ..."
puts Benchmark.measure { print nats_range.take(10**7).drop(10**7-10), "\n" }

puts "#{'_'*79+"\n"}Benchmarking Enumerable methods on Enumerator ..."
puts Benchmark.measure { print nats_enum.take(10**7).drop(10**7-10), "\n" }


$ ruby a.rb 
_______________________________________________________________________________
Benchmarking Enumerable methods on Range ...
[9999991, 9999992, 9999993, 9999994, 9999995, 9999996, 9999997, 9999998, 9999999, 10000000]
  1.570000   0.010000   1.580000 (  1.576761)
_______________________________________________________________________________
Benchmarking Enumerable methods on Enumerator ...
[9999991, 9999992, 9999993, 9999994, 9999995, 9999996, 9999997, 9999998, 9999999, 10000000]
 15.620000   0.020000  15.640000 ( 15.665156)

使用枚举器的等效代码要慢10倍!

我在这里问,有没有人能解释这个巨大的差异。我是否错误地使用了枚举数?在当前的Ruby实现中,这是一种已知的回归吗?

MRI Ruby 1.9.3p385

EN

回答 2

Stack Overflow用户

发布于 2013-02-21 06:12:50

Enumerators基于Fibers,您可以将其视为非常轻量级的线程。(实际上,它们是协程。)

Range使用succ进行迭代,使用<=确定是否已经到达终点。

因此,您的Range示例使用了2000万个对Fixnum#succFixnum#<=的方法调用,这两个方法都经过了大量优化,基本上或多或少直接映射到相应的汇编指令。

您的Enumerator示例使用了2000万次对Enumerator::Yielder#<< (谁知道这有多昂贵)和Fixnum#+的调用,以及1000万次Fiber上下文切换。我可以很容易地想象一个Fiber上下文切换比一个简单的Fixnum操作要贵10倍。

票数 3
EN

Stack Overflow用户

发布于 2013-02-21 07:07:00

我不认为你问对了问题。

如果你需要10个元素,并且正在生成和存储100亿个元素,那么你的算法很可能在一开始就有问题。

此外,当Range不可能给出您想要的答案时,将EnumeratorRange进行比较是没有意义的。

运行微基准测试很有趣,但我们要记住,它们通常是无意义的。无论如何,这是我得到的一个更合理的限制。

代码语言:javascript
复制
class AllNumbers
  include Enumerable
  def each
    i = 0
    loop { yield i += 1 }
  end
end

custom = AllNumbers.new
enum = Enumerator.new do |y|
  i=0
  loop { y << i+=1 }
end
range = 1..Float::INFINITY
require 'fruity'

limit = 1000
compare do
  using_range        {  range.take(limit) }
  using_enumerator   {   enum.take(limit) }
  using_custom_class { custom.take(limit) }
end

结果让我有点惊讶:

代码语言:javascript
复制
using_custom_class is faster than using_range by 20.0% ± 10.0%
using_range is faster than using_enumerator by 70.0% ± 10.0%

我不会猜到的。实际上,虽然+=是优化的,但Range#each并没有针对整数进行优化。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14988547

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档