簡體   English   中英

在Ruby中加快Date#parse和Date#strptime的速度,采用更優雅的方式還是最佳實踐?

[英]Speed up Date#parse & Date#strptime in Ruby, more elegant way or best practice?

這個問題來自另一個處理日期格式為string的大文本的性能問題。

將csv文件中的數據加載到ruby數組中后,效率最低的部分是將那些360,000日期格式的字符串對象解析為日期對象。 這需要超過50%的CPU時間。

關於在SO中將字符串解析為日期的最有效方法存在一些問題。 但是它們大多數都已過時,並且沒有一個人認為在所有360,000條記錄中實際上應該只解析5個日期對象的情況。

更常見的是,對於企業應用程序而言,所需的所有日期可能都在5年或10年內,即大約2,000至4,000個日期。 如果一天只需要從文件或數據庫中獲取100條數據記錄,則無需使用99%的CPU時間來解析日期和創建日期對象。

這是我的嘗試

定義一個StaticDate類,以通過存儲之前解析的日期對象來提高性能。

require 'date'
class StaticDate
  @@all={}
  def self.instance(p1 = nil, p2 = nil, p3 = nil, p4 = Date::JULIAN)
    @@all[p1*10000+p2*100+p3] ||= Date.new p1, p2, p3, p4
  end

  def self.parse( date_str)
    @@all[date_str] ||= Date.parse date_str
  end

  def self.strptime( date_str, format_str)
    @@all[date_str + format_str] ||= Date.strptime date_str, format_str
  end
end

我的問題

我知道我的代碼有重復相同功能類的難聞的氣味,但是在這種360,000條記錄的情況下, Date#strptime速度提高了13倍, Date#parse速度提高了41倍。 因此,我認為值得改進和重構:

  • 有沒有gem或plugin已經以更優雅的方式實現了這些東西? 或任何改進或重構這些代碼的建議都值得贊賞。
  • 既然我們都知道所有紅寶石日期對象都是不可變的。 您是否認為有必要將這些功能擴展到紅寶石日期類?
  • 在Rails應用程序中是否還有其他最佳實踐來獲得最佳的日期對象操作性能? (如果您認為范圍太廣,請忽略此問題。)

當然,我做錯了事,而且我不是英語,所以對提高班級或這個問題的任何幫助將不勝感激。

提前致謝

我嘗試的基准

我創建了一個包含360,000行的輸入數組,而不是從文件加載數據:

a= [['a', '2014-6-1', '1'],
    ['a', '2014-6-2', '2'],
    ['a', '2014-6-4', '3'],
    ['a', '2014-6-5', '4'],
    ['b', '2014-6-1', '1'],
    ['b', '2014-6-2', '2'],
    ['b', '2014-6-3', '3'],
    ['b', '2014-6-4', '4'],
    ['b', '2014-6-5', '5']]*40000

基准代碼:

b=a.map{|x| x + x[1].split('-').map(& :to_i) }
Benchmark.bm {|x|
  x.report('0. Date#New 1 date '){ 360000.times{ Date.new(2014,1,1)} }
  x.report('1. Date#New        '){ b.each{ |x| Date.new(x[3],x[4],x[5])} }
  x.report('2. Date#Strptime   '){ a.each{ |x| Date.strptime(x[1],"%Y-%m-%d")} }
  x.report('3. Date#Parse      '){ a.each{ |x| Date.parse(x[1])} }
  x.report('4. StaticDate#New  '){ b.each{ |x| StaticDate.instance( x[3],x[4],x[5]) } }
  x.report('5. StaticDate#StrP '){ a.each{ |x| StaticDate.strptime(x[1],"%Y-%m-%d")} }
  x.report('6. StaticDate#Parse'){ a.each{ |x| StaticDate.parse(x[1])} }
  x.report('7. split to date   '){ a.each{ |x| Date.new(*(x[1].split('-').map(& :to_i)))} }

}  

基准測試結果:

                         user     system      total        real
0. Date#New 1 date   0.297000   0.000000   0.297000 (  0.299017)
1. Date#New          0.390000   0.000000   0.390000 (  0.384022)
2. Date#Strptime     2.293000   0.000000   2.293000 (  2.306132)
3. Date#Parse        7.113000   0.000000   7.113000 (  7.101406)
4. StaticDate#New    0.188000   0.000000   0.188000 (  0.188011)
5. StaticDate#StrP   0.546000   0.000000   0.546000 (  0.558032)
6. StaticDate#Parse  0.171000   0.000000   0.171000 (  0.167010)
7. split to date     1.623000   0.000000   1.623000 (  1.641094)

根據Date文檔

所有日期對象都是不可變的。 因此無法修改自己。

如果從字符串創建日期實例是您的瓶頸,則可以使用哈希來創建和存儲它們:

date_store = Hash.new { |h, k| h[k] = Date.strptime(k, '%Y-%m-%d') }

date_store['2014-6-1'] #=> #<Date: 2014-06-01 ((2456810j,0s,0n),+0s,2299161j)>
date_store['2014-6-2'] #=> #<Date: 2014-06-02 ((2456811j,0s,0n),+0s,2299161j)>
date_store['2014-6-3'] #=> #<Date: 2014-06-03 ((2456812j,0s,0n),+0s,2299161j)>

所有結果都保存在哈希中:

date_store
#=> {"2014-6-1"=>#<Date: 2014-06-01 ((2456810j,0s,0n),+0s,2299161j)>,
#    "2014-6-2"=>#<Date: 2014-06-02 ((2456811j,0s,0n),+0s,2299161j)>,
#    "2014-6-3"=>#<Date: 2014-06-03 ((2456812j,0s,0n),+0s,2299161j)>}

提取已知密鑰只是一個查找,不執行任何解析,也不必創建新的Date實例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM