加速日期#parse&Date#strptime在Ruby中,更优雅的方式还是最佳实践?

此问题源于处理带有日期格式字符串的大型文本的另一个性能问题。

将数据从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 

我的问题

我知道我的代码有重复同一个function类的难闻气味,但在这个360,000条记录的情况下, Date#strptime速度提高了13倍, Date#parse速度提高了41倍。 所以我认为改进和重构是非常值得的:

  • 是否有任何gem或插件已经以更优雅的方式实现这些东西? 或者任何改进或重构这些代码的建议都表示赞赏。
  • 因为我们都知道所有ruby日期对象都是不可变的。 您是否认为将这些function扩展到ruby日期类是必要的?
  • 是否还有其他最佳实践可以在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_store['2014-6-2'] #=> # date_store['2014-6-3'] #=> # 

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

 date_store #=> {"2014-6-1"=>#, # "2014-6-2"=>#, # "2014-6-3"=>#} 

获取已知密钥仅仅是查找,不执行解析并且不必创建新的Date实例。