【速度測試】SketchUp圖元類型判斷的速度對比
上一篇文章[SU-2022-04]中提到,使用 typename 來判斷圖元類型,由于涉及字符串的對比,執(zhí)行效率較低,因而推薦使用 grep 或者 is_a? 方法。但是這兩種判斷方式在迭代圖元的過程中的執(zhí)行效率究竟差多少?為了解答這個問題,本文做一個實驗測試一下。
一、圖元類型判斷的方法
判斷一個圖元是否屬于某種類型有以下幾種方法,第一種方法直接判斷圖元類型,這一種方法可用的語法很多,至少包括以下幾種:
#令 ent 為一個圖元puts ent.typename == "Edge"puts ent.is_a?(Sketchup::Edge)puts ent.instance_of?(Sketchup::Edge)puts Sketchup::Edge === ent
以上均能夠判斷圖元 ent 是否是一個邊線類圖元,區(qū)別在于:第一種基于字符串的比較,等號左邊可能是 "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# 不需要判斷圖元類型直接執(zhí)行rescue# 如果報錯了不會拋出異常而是執(zhí)行這里的代碼,這里也可以是空的end
二、測試不同方法的運行速度
這三種方法具體又可以分為不同的實現路徑,以下選取幾種方法,分別測試以下幾段代碼,測試其執(zhí)行速度:
#對比class的方法GC.starttz=Time.now().to_fedge_longer_than_10_meters=0Sketchup.active_model.entities.to_a.each{|ent|if ent.class==Sketchup::Edge thenif ent.length>10.m thenedge_longer_than_10_meters+=1endend}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) thenif ent.length>10.m thenedge_longer_than_10_meters+=1endend}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" thenif ent.length>10.m thenedge_longer_than_10_meters+=1endend}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 thenedge_longer_than_10_meters+=1end}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) thenif ent.length>10.m thenedge_longer_than_10_meters+=1endend}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) thenif ent.length>10.m thenedge_longer_than_10_meters+=1endend}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|beginif ent.length>10.m thenedge_longer_than_10_meters+=1endrescueend}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 doent=Sketchup.active_model.entities[index]if ent.is_a?(Sketchup::Edge) thenif ent.length>10.m thenedge_longer_than_10_meters+=1endendindex+=1endputs "長度大于10米的邊線圖元數量:#{edge_longer_than_10_meters}"puts "總耗時:#{Time.now().to_f-tz}秒"
三、測試結果
通過運行上一部分中的代碼,測試結果如下圖所示:

從測試結果來看,有以下幾條結論:
(1)使用ruby對象類型的判斷方法要比字符串判斷方法節(jié)約30%左右的時間。
(2)在不涉及修改圖元的情況下使用 while 語句非但沒有提速,反而速度較慢,因次沒有必要用它替代迭代器。
(3)判斷是否擁有成員的 respond_to? 方法速度也與判斷類型相近,因此對于上文所說的群組組件的判斷,用此方法更為理想。
(4)有隔離影響功能的 to_a 方法會額外增加一小部分執(zhí)行時間,因此使用 grep 方法既可以獲得較高的執(zhí)行效率,同時還可以做到隔離影響,是相對而言最合理的方法。
(5)處理異常的 rescue 語句,速度低于字符串的比較,這是毫不意外的,實現具體功能時還是應該盡量避免本身可控的不確定性。
(完)
本文編號:SU-2022-05
