【速度测试】SketchUp图元类型判断的速度对比

Apiglio

共 4174字,需浏览 9分钟

 · 2022-09-17

上一篇文章[SU-2022-04]中提到,使用 typename 来判断图元类型,由于涉及字符串的对比,执行效率较低,因而推荐使用 grep 或者 is_a? 方法。但是这两种判断方式在迭代图元的过程中的执行效率究竟差多少?为了解答这个问题,本文做一个实验测试一下。

一、图元类型判断的方法

判断一个图元是否属于某种类型有以下几种方法,第一种方法直接判断图元类型,这一种方法可用的语法很多,至少包括以下几种:

#令 ent 为一个图元puts ent.typename == "Edge"puts ent.is_a?(Sketchup::Edge)puts ent.instance_of?(Sketchup::Edge)puts Sketchup::Edge === ent

以上均能够判断图元 ent 是否是一个边线类图元,区别在于:第一种基于字符串的比较,等号左边可能是 "Edge"、 "Face" 或者 "Group" 等字符串,通过和右边的 "Edge" 对比确认 ent 是否为边线类。余下三种都是基于ruby类的判断,通过比较 ent 是否属于 Sketchup::Edge 类来判断。其中第三种方法中的 instance_of? 需要图元必须是 Sketchup::Edge 也不能为其子类,但是对于 SketchUp 模型的情况而言,不存在上述情况,所以可以认为是等价的。

第二种方法是判断其是否拥有某些成员,例如边线类都拥有长度 length 成员,平面类都拥有面积 area 成员:

#令 ent 为一个图元puts ent.respond_to?(:length)puts ent.methods.include?(:length)

这种方法的好处在于其清晰的目标导向性,判断图元类型的很大一部分原因是为了避免类型不符的图元抛出 NoMethodError 错误,从而导致脚本意外终止。并且,对于某些特殊的情况可以很方便的表达,例如需要同时选出组件、群组和图片这三类图元时,由于这三种图元对象都包括 :definition 方法,所以可以直接用以下方式判断:

puts ent.respond_to?(:definition)

第三种方法是直接不进行判断,将可能的抛出的异常用 begin rescue end 的方法来处理,这不是一个负责任的做法,但是某些时候可以用来跳过一些比较麻烦的筛选问题。

begin  # 不需要判断图元类型直接执行rescue  # 如果报错了不会抛出异常而是执行这里的代码,这里也可以是空的end


二、测试不同方法的运行速度

这三种方法具体又可以分为不同的实现路径,以下选取几种方法,分别测试以下几段代码,测试其执行速度:

#对比class的方法GC.starttz=Time.now().to_fedge_longer_than_10_meters=0Sketchup.active_model.entities.to_a.each{|ent|  if ent.class==Sketchup::Edge then    if ent.length>10.m then      edge_longer_than_10_meters+=1    end  end}puts "长度大于10米的边线图元数量:#{edge_longer_than_10_meters}"puts "总耗时:#{Time.now().to_f-tz}秒"
#使用is_a?的方法GC.starttz=Time.now().to_fedge_longer_than_10_meters=0Sketchup.active_model.entities.to_a.each{|ent| if ent.is_a?(Sketchup::Edge) then if ent.length>10.m then edge_longer_than_10_meters+=1 end end}puts "长度大于10米的边线图元数量:#{edge_longer_than_10_meters}"puts "总耗时:#{Time.now().to_f-tz}秒"
#对比typename的方法GC.starttz=Time.now().to_fedge_longer_than_10_meters=0Sketchup.active_model.entities.to_a.each{|ent| if ent.typename=="Edge" then if ent.length>10.m then edge_longer_than_10_meters+=1 end end}puts "长度大于10米的边线图元数量:#{edge_longer_than_10_meters}"puts "总耗时:#{Time.now().to_f-tz}秒"
#使用grep的方法GC.starttz=Time.now().to_fedge_longer_than_10_meters=0Sketchup.active_model.entities.grep(Sketchup::Edge).each{|ent| if ent.length>10.m then edge_longer_than_10_meters+=1 end}puts "长度大于10米的边线图元数量:#{edge_longer_than_10_meters}"puts "总耗时:#{Time.now().to_f-tz}秒"
#判断成员的方法GC.starttz=Time.now().to_fedge_longer_than_10_meters=0Sketchup.active_model.entities.to_a.each{|ent| if ent.respond_to?(:length) then if ent.length>10.m then edge_longer_than_10_meters+=1 end end}puts "长度大于10米的边线图元数量:#{edge_longer_than_10_meters}"puts "总耗时:#{Time.now().to_f-tz}秒"
#不使用to_a的is_a?方法GC.starttz=Time.now().to_fedge_longer_than_10_meters=0Sketchup.active_model.entities.each{|ent| if ent.is_a?(Sketchup::Edge) then if ent.length>10.m then edge_longer_than_10_meters+=1 end end}puts "长度大于10米的边线图元数量:#{edge_longer_than_10_meters}"puts "总耗时:#{Time.now().to_f-tz}秒"
#处理异常的方法GC.starttz=Time.now().to_fedge_longer_than_10_meters=0Sketchup.active_model.entities.to_a.each{|ent| begin if ent.length>10.m then edge_longer_than_10_meters+=1 end rescue end}puts "长度大于10米的边线图元数量:#{edge_longer_than_10_meters}"puts "总耗时:#{Time.now().to_f-tz}秒"
#使用while的方法GC.starttz=Time.now().to_fedge_longer_than_10_meters=0index=0len=Sketchup.active_model.entities.lengthwhile index<len do ent=Sketchup.active_model.entities[index] if ent.is_a?(Sketchup::Edge) then if ent.length>10.m then edge_longer_than_10_meters+=1 end end index+=1endputs "长度大于10米的边线图元数量:#{edge_longer_than_10_meters}"puts "总耗时:#{Time.now().to_f-tz}秒"


三、测试结果

通过运行上一部分中的代码,测试结果如下图所示:


从测试结果来看,有以下几条结论:

(1)使用ruby对象类型的判断方法要比字符串判断方法节约30%左右的时间。

(2)在不涉及修改图元的情况下使用 while 语句非但没有提速,反而速度较慢,因次没有必要用它替代迭代器。

(3)判断是否拥有成员的 respond_to? 方法速度也与判断类型相近,因此对于上文所说的群组组件的判断,用此方法更为理想。

(4)有隔离影响功能的 to_a 方法会额外增加一小部分执行时间,因此使用 grep 方法既可以获得较高的执行效率,同时还可以做到隔离影响,是相对而言最合理的方法。

(5)处理异常的 rescue 语句,速度低于字符串的比较,这是毫不意外的,实现具体功能时还是应该尽量避免本身可控的不确定性。

(完)



本文编号:SU-2022-05


浏览 42
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报