Ruby 每个 vs while 循环性能
Ruby each vs while loop performance
试图解决Ruby中算法的一个基本问题,并测试性能。
为了以防万一,该算法旨在找到可被 1 到 20 之间的所有数字整除的最小正数。
这是代码:
def remainder(number) # with while
divisor = 2
while divisor < 21
return false unless number % divisor == 0
divisor += 1
end
true
end
def remainder(number) # with each
(2..20).each do |divisor|
return false unless number % divisor == 0
end
true
end
number = 180_000_000
while number < 10_000_000_000
if remainder number
puts "#{number}"
break
end
number += 1
end
在我的电脑上,while 版本 Ruby 大约需要 10 秒,每个版本需要 70 到 80 秒才能解决。代码做完全相同的事情,给出相同的结果。为什么性能差异如此之大?
each
是一种方法,它是 using while
在 C 中使用 for
循环实现的,(在 C 中)和你 while
循环做的一样。在内部它需要持有一个计数器,将其初始化为 2
并将其递增到 20
- 这与您的 while
版本需要做的事情相同。但是 each
版本也有创建函数(块)的开销,将它发送到 each
方法,并在 for
循环的每次迭代中调用它 each
方法的实现。所有这些都需要计算机进行额外的工作,这会使代码变慢。
费用好像是加了:
- 为范围对象 (2..20) 创建枚举器
- 在
each
中调用块
这是一个基准
require 'benchmark'
c = 100_000
Benchmark.bm(7) do |x|
x.report("range - 1 :") { c.times { (2..20) } }
x.report("range - 2 :") { c.times { (2..20).each } }
x.report("range - 3 :") { c.times { (2..20).each { |x| x } } }
end
上面的示例输出是:
user system total real
range - 1 : 0.000000 0.000000 0.000000 ( 0.006004)
range - 2 : 0.031000 0.000000 0.031000 ( 0.026017)
range - 3 : 0.125000 0.000000 0.125000 ( 0.122081)
[Finished in 0.4s]
可以看出,创建 Range 对象不是问题,但是为它创建一个枚举器会增加时间,并将一个块传递给该迭代器并执行一些代码,会进一步增加成本。
与此相比,while
循环实现执行的是原始操作。因此,速度更快。
请注意,for
循环的性能与 each
一样糟糕,因为它或多或少等同于 each
实现
试图解决Ruby中算法的一个基本问题,并测试性能。
为了以防万一,该算法旨在找到可被 1 到 20 之间的所有数字整除的最小正数。 这是代码:
def remainder(number) # with while
divisor = 2
while divisor < 21
return false unless number % divisor == 0
divisor += 1
end
true
end
def remainder(number) # with each
(2..20).each do |divisor|
return false unless number % divisor == 0
end
true
end
number = 180_000_000
while number < 10_000_000_000
if remainder number
puts "#{number}"
break
end
number += 1
end
在我的电脑上,while 版本 Ruby 大约需要 10 秒,每个版本需要 70 到 80 秒才能解决。代码做完全相同的事情,给出相同的结果。为什么性能差异如此之大?
each
是一种方法,它是 using 在 C 中使用 while
for
循环实现的,(在 C 中)和你 while
循环做的一样。在内部它需要持有一个计数器,将其初始化为 2
并将其递增到 20
- 这与您的 while
版本需要做的事情相同。但是 each
版本也有创建函数(块)的开销,将它发送到 each
方法,并在 for
循环的每次迭代中调用它 each
方法的实现。所有这些都需要计算机进行额外的工作,这会使代码变慢。
费用好像是加了:
- 为范围对象 (2..20) 创建枚举器
- 在
each
中调用块
这是一个基准
require 'benchmark'
c = 100_000
Benchmark.bm(7) do |x|
x.report("range - 1 :") { c.times { (2..20) } }
x.report("range - 2 :") { c.times { (2..20).each } }
x.report("range - 3 :") { c.times { (2..20).each { |x| x } } }
end
上面的示例输出是:
user system total real
range - 1 : 0.000000 0.000000 0.000000 ( 0.006004)
range - 2 : 0.031000 0.000000 0.031000 ( 0.026017)
range - 3 : 0.125000 0.000000 0.125000 ( 0.122081)
[Finished in 0.4s]
可以看出,创建 Range 对象不是问题,但是为它创建一个枚举器会增加时间,并将一个块传递给该迭代器并执行一些代码,会进一步增加成本。
与此相比,while
循环实现执行的是原始操作。因此,速度更快。
请注意,for
循环的性能与 each
一样糟糕,因为它或多或少等同于 each
实现