2011-06-21 75 views
3

我正在使用Ruby on Rails 3.0.7,我想迭代一個對象數組(除了元素id等於1id引用到陣列[1]索引)。性能:遍歷數組除外元素

我知道我可以使用if語句「內部」到一個each語句,並且檢查每個「當前」「考慮」元素if id == 1。但是,由於該數組填充了大量數據,因此我希望找到另一種方式以更高性能的方式完成相同的任務(避免每次運行if)。

我該怎麼辦?

+0

爲什麼不確保數組沒有填入1開頭的ID? –

+1

爲什麼ruby中沒有array.except元素方法? –

回答

1
a = ['a', 'b', 'c'] 
a.each_with_index.reject {|el,i| i == 1}.each do |el,i| 
    # do whatever with the element 
    puts el 
end 

恕我直言,做一個更好的選擇方式,而不是使用自己的顯式if語句。然而,我相信它會產生與使用if大致相同的性能,甚至可能略低。

如果經過基準測試,其他人都建議您知道這個過程所花費的時間肯定比您可以允許的時間慢,而且這種選擇會導致速度慢,那麼可以很容易地修改這個選項以刪除大量的選擇方式:

a = ['a', 'b', 'c'] 
n = 1 
(a.first(n) + a.drop(n + 1)).each do |el| 
    # do whatever with the element 
    puts el 
end 

不幸的是我相信這也會比運行簡單的if要慢。我相信一個可能有速度的潛力是:

a = ['a', 'b', 'c'] 
n = 1 
((0...n).to_a+((n+1)...a.size).to_a).map{|i| a[i]}.each do |el| 
    # do whatever with the element 
    puts el 
end 

但是這又很有可能會變慢。

編輯

Benchmark是在this gist。這些結果實際上讓我感到驚訝,拒絕是迄今爲止最慢的選擇,其次是範圍。在完全不移除元素之後的最高性能是使用firstdrop來選擇它周圍的所有元素。

結果爲不使用選擇作爲基準的百分比:

with if    146% 
with first and drop 104% 
without if   100% 

顯然,這是高度依賴於你的元素做什麼,這是與可能是最快的運作紅寶石可以進行測試。操作越慢,這些差異就越小。一如既往:基準測試,基準測試,基準測試

7
  1. 製作工作方案
  2. 簡介
  3. 優化

Donald Knuth said:

我們應該忘記小 效率,講的 時間約97%:過早的優化是所有e的 的根VIL。現在

,你可以做這樣的事情:

def f 
    do_something 
end 

f 0 
for i in 2..n 
    f i 
end 

甚至:

def f 
    yield 0 
    for i in [email protected] 
    yield i 
    end 
end 

f do |i| 
    do_something 
end 

但你可能不希望做這種事,如果你確實,只有在發現它很重要之後。

最後,假設這個醜陋的技巧實際上會讓您的服務器運行得更快一些。它值得嗎?

+2

+1,正是我想說的。 '如果id == 1'通常會非常高效。除非您發現實際的性能問題,否則不要冒汗,然後啓動您的分析器。 –

1

if聲明是一個非常便宜的操作。您可以使用標準基準測試工具來檢查。

require "benchmark" 

array = [1] * 100_000 

Benchmark.bm do |bm| 
    bm.report "with if" do 
    array.each_with_index do |element, i| 
     next if i == 1 
     element - 1 
    end 
    end 

    bm.report "without if" do 
    array.each do |element| 
     element - 1 
    end 
    end 
end 

結果:

   user  system  total  real 
with if  0.020000 0.000000 0.020000 ( 0.018115) 
without if 0.010000 0.000000 0.010000 ( 0.012248) 

它是在一個100個000元件陣列約0.006第二差值。除非它成爲瓶頸,否則你不應該在意這一點,我懷疑它會如何。

1

測試實際的for循環可能會花費五分鐘的時間。它可能在Ruby界被皺起眉頭,但這並不意味着它永遠不值得使用。當你調用each或map或者其他的方法時,這些方法以任何方式使用for循環。避免絕對。

這也取決於數組可以有多大,在某些n上,一個可能會比另一個快。在這種情況下,這絕對不值得。

如果你不需要一個特定的元素,也許你不需要將該行數據存儲在數據庫中。第1行和其餘行之間有什麼區別,換句話說,你爲什麼跳過它? id = 1的行是否始終具有相同的數據?如果是這樣,將其存儲爲常量可能會更好,並會使您的問題沒有意義。性能幾乎總是花費更多的內存。

除非Rails 3中做不同的事情,和你拉出來的數據,並使用id作爲取景器鍵,ID = 1會在元素0

不幸的是,Knuth的報價被曲解了很多,習慣請原諒那些糟糕的,效率低下的代碼,如果程序員受過足夠的教育,那麼這些代碼就不會被編寫出來,然後考慮5秒鐘。當然,花一個星期的時間來加速你不知道的代碼是一個問題還是一個小問題,但這更多的是Knuth所談論的。性能是計算機科學中最容易被誤解和濫用的概念之一。