[英]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倍。 因此,我認為值得改進和重構:
當然,我做錯了事,而且我不是英語,所以對提高班級或這個問題的任何幫助將不勝感激。
提前致謝
我創建了一個包含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.