查看: 3423|回复: 4

[讨论] 【效率测试】Ruby中include方法的效率问题

[复制链接]

寒

梦石
0
星屑
1049
在线时间
1391 小时
注册时间
2010-8-9
回帖
3301
发表于 2015-1-28 13:08:14 | 显示全部楼层 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有账号?注册会员

×
本帖最后由 寒冷魔王 于 2015-1-28 13:18 编辑

昨天重构了SMRC,新建类Point表示点的坐标,期间运用到了include方法。发现这个版本比上次发布的版本效率要快1倍。
今天调试了半天,终于找到了原因:include方法判断的内容:数组还是类。
如下发布测试代码:
  1. class Test
  2.   def self.test(string,&block)
  3.     time = Time.new
  4.     block.call
  5.     puts string,Time.new-time
  6.   end
  7. end

  8. class Point
  9.   attr_accessor :x, :y
  10.   def initialize(x, y)
  11.     @x = x
  12.     @y = y
  13.   end
  14.   def ==(point)
  15.     return @x==point.x && @y==point.y
  16.   end
  17. end

  18. point1 = Array.new
  19. point2 = Array.new
  20. max = 10000000

  21. Test.test("Push Class Point") { max.times { |i| point1.push Point.new(i,i) } }
  22. Test.test("Push Class Array") { max.times { |i| point2.push [i,i] } }
  23. Test.test("Include Class Point") { point1.include?(Point.new(max,0)) } # 取最坏情况
  24. Test.test("Include Class Array") { point2.include?([max,0]) }
复制代码
结果
  1. Push Class Point
  2. 3.588006
  3. Push Class Array
  4. 2.277604
  5. Include Class Point
  6. 1.060802
  7. Include Class Array
  8. 2.168404
复制代码
这个测试表明,使用include方法判断数组时,效率要比判断类(在类内内建方法"==")慢得多。虽然构建对象时,数组对象的构建速度要快一些。

(注:如果想要测试,请使用控制台窗口运行,否则发生死机现象与本人无关。)

围观:@taroxd  @RyanBern  

点评

嗯对~  发表于 2015-1-28 23:08
这个明明是叫做include?方法。。。  发表于 2015-1-28 21:58
梦石
0
星屑
9557
在线时间
5074 小时
注册时间
2013-6-21
回帖
3459

开拓者贵宾剧作品鉴家

发表于 2015-1-28 14:24:57 | 显示全部楼层
我的测试结果:
ss.png
因为机子太慢我改成5000000了TAT

啥?为啥不一样?因为我改了源码。
[pre lang="Ruby"]class Test
  def self.test(string,&block)
    time = Time.new
    block.call
    puts string,Time.new-time
  end
end

class Point
  attr_accessor :x, :y
  def initialize(x, y)
    @x = x
    @y = y
  end
  def ==(point)
    return @x==point.x && @y==point.y
  end
end

class Array
  def point_include?(point_arr)
    self.each do |point|
      return true if point[0] == point_arr[0] && point[1] == point_arr[1]
    end
    return false
  end
end


point1 = Array.new
point2 = Array.new
max = 5000000

Test.test("Push Class Point") { max.times { |i| point1.push Point.new(i,i) } }

Test.test("Push Class Array") { max.times { |i| point2.push [i,i] } }

Test.test("Include Class Point") { point1.include?(Point.new(max,0)) } # 取最坏情况

Test.test("Include Class Array") { point2.point_include?([max,0]) }
[/pre]
总觉得Array#==方法太不自然了。

点评

Array也是一个对象  发表于 2015-1-28 15:44
话说Ruby真是想什么就有什么,我只是试着clone一下就成功了  发表于 2015-1-28 15:40
我用Array=Array.clone在类的内部创建了一个副本,这样就不会影响到外面了~  发表于 2015-1-28 15:38
+1,这样改来,还是Array快一点。= =不过还要袭击内部类,真是各种蛋疼。。。  发表于 2015-1-28 14:41

评分

参与人数 2星屑 +190 收起 理由
恐惧剑刃 + 150 认可答案
寒冷魔王 + 40 帅气

查看全部评分

回复

使用道具 举报

寒

梦石
0
星屑
1049
在线时间
1391 小时
注册时间
2010-8-9
回帖
3301
 楼主| 发表于 2015-1-28 14:58:10 | 显示全部楼层
本帖最后由 寒冷魔王 于 2015-1-28 14:59 编辑
RyanBern 发表于 2015-1-28 14:24
我的测试结果:

因为机子太慢我改成5000000了TAT


我的那段程序的三个改法:(地图100*100,移动100)
捕获14.PNG
第一个是使用数组+默认include的结果
第二个是我用Point类优化的结果
第三个是用你的方法更改我的程序所得的结果

第二三个差不多,第一个差许多。。果然Ruby的Array效率低啊。。

点评

Ruby是不是用Ruby的迭代实现的include啊。。我试试。。  发表于 2015-1-28 15:01
由于不知道Array#include?和Array#==是如何实现的,所以我也不清楚为啥会这么慢  发表于 2015-1-28 14:59
回复

使用道具 举报

寒

梦石
0
星屑
1049
在线时间
1391 小时
注册时间
2010-8-9
回帖
3301
 楼主| 发表于 2015-1-28 15:06:03 | 显示全部楼层
本帖最后由 寒冷魔王 于 2015-1-28 15:26 编辑
RyanBern 发表于 2015-1-28 14:24
我的测试结果:

因为机子太慢我改成5000000了TAT


[pre lang="ruby" line="1"]class Test
  def self.test(string,&block)
    time = Time.new
    block.call
    puts string,Time.new-time
  end
end

point = Array.new
max = 10000000

Test.test("Push Class Array") { max.times { |i| point.push [i,i] } }
Test.test("Include Class Array") { point.include?([max,0]) } # 取最坏情况
Test.test("Do Class Array") { point.each { |p| break if p==[max,0] } # 迭代+判断 }
Test.test("Do2 Class Array") { point.each { |p| } } # 纯迭代
Test.test("Do3 Class Array") { point.each { |p| a=p;a==p } } # 赋值+判断自身引用
Test.test("Each_Index Class Array")  { point.each_index { |i| } } # Each_Index迭代
#Test.test("Each_Index2 Class Array") { point.each_index { |i| p==[i,i] } }  # !!卡死
Test.test("For Class Array")  { for i in 0...point.size; end  } # For迭代  [/pre]结果
[pre lang="ruby" line="1"]Push Class Array
2.076804
Include Class Array
2.170404
Do Class Array
3.902007
Do2 Class Array
0.624001
Do3 Class Array
1.113064
Each_Index Class Array
0.622035
For Class Array
0.641037[/pre]

感觉而言,Include 和 迭代+Ruby实现的判断,差不多。
根据以上测试,include效率低下应该是Array自身的判断==问题。
Array先判断是否是自身引用,然后再判断数组元素。

总而言之,Ruby的Array类是个效率非常低的类= =

点评

所以应该是Array自身的判断==问题。Array先判断是否是自身引用,然后再判断数组元素。  发表于 2015-1-28 15:19
Test.test("Each_Index2 Class Array") { point.each_index { |i| p==[i,i] } } 这一行不要试,因为卡死了。。  发表于 2015-1-28 15:18
其中赋值需要0.1s  发表于 2015-1-28 15:17
Test.test("Do3 Class Array") { point.each { |p| a=p;a==p } } 结果是1.113064  发表于 2015-1-28 15:14
那估计是Array#==太慢了?  发表于 2015-1-28 15:11
回复

使用道具 举报

…あたしは天

梦石
0
星屑
2308
在线时间
4033 小时
注册时间
2010-10-4
回帖
10548

开拓者贵宾

发表于 2015-1-30 07:50:19 | 显示全部楼层
本帖最后由 taroxd 于 2015-1-30 07:56 编辑

为何不用 Hash 或者 Set?

顺便提一句,数组在算 hash 的时候也很慢。

[pre lang="ruby" line="1"]
require 'benchmark'

time = 10_000_000

default_proc = -> h, k { h[k] = Hash.new(&default_proc) }

hash = Hash.new(&default_proc)

array_key = 1,2,3,4,5

hash[array_key] = hash[1][2][3][4][5] = 0

Benchmark.bmbm do |x|
  x.report("embed key") { time.times { hash[1][2][3][4][5] } }
  x.report("array key") { time.times { hash[array_key] } }
end

=begin
Rehearsal ---------------------------------------------
embed key   1.700000   0.000000   1.700000 (  1.704098)
array key  14.633000   0.000000  14.633000 ( 14.631836)
----------------------------------- total: 16.333000sec

                user     system      total        real
embed key   1.669000   0.000000   1.669000 (  1.665095)
array key  14.493000   0.000000  14.493000 ( 14.530831)
=end[/pre]

点评

Hash在处理某些问题上没数组方便,而且看ruby源代码发现貌似Array是零散的。与其这么用不如直接C++。话说有没有连续的数组?  发表于 2015-1-30 12:41

评分

参与人数 1星屑 +150 收起 理由
恐惧剑刃 + 150 认可答案

查看全部评分

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部