在 Rails 中使 TimeWithZone 对象变得惰性

Posted

技术标签:

【中文标题】在 Rails 中使 TimeWithZone 对象变得惰性【英文标题】:Make TimeWithZone objects lazy in Rails 【发布时间】:2012-04-06 00:36:45 【问题描述】:

如何让ActiveSupport::TimeWithZone 更快地构建或懒惰?

我一直在分析我的 Rails 应用程序,我发现三分之一的 CPU 时间用于构建这些 TimeWithZone 对象。我对此束手无策。看似简单的时间对象怎么可能构建起来如此昂贵?

这是每个请求运行无数次的代码:

def deserialize_from_cache(json)
  attributes = ActiveSupport::JSON.decode(json)
  attributes.keys.to_a.each do |k|
    v = attributes[k]
    if v.is_a? Array and v.length == 2 and v[0] == 'Time'
      attributes[k] = Time.at(v[1]).in_time_zone # This is the expensive code
    end
  end
  self.allocate.init_with('attributes' => attributes)
end

我对普通的旧 Time 对象构造进行了基准测试,发现它比 TimeWithZone 构造快一个数量级:

puts Benchmark.measure  200000.times  Time.at(1330367843)  
  0.070000   0.000000   0.070000 (  0.068956)

puts Benchmark.measure  200000.times  Time.at(1330367843).in_time_zone  
  0.720000   0.000000   0.720000 (  0.715802)

我可以做些什么以编程方式将所有模型的日期时间属性替换为懒惰的 TimeWithZone 对象,这些对象是普通的旧(且便宜)Time 对象,直到它们被使用时它们变成 @ 987654329@ 对象?这远远超出了我的 Ruby 能力。

【问题讨论】:

您的基准测试在我的笔记本电脑上给出了完全不同的结果(Thinkpad Ubuntu 11.10 Ruby 1.9.3 p125,Rails 3.2.3)。我看到#at 方法和#in_time_zone 花费的时间几乎完全相同。您的代码的结果: Time.at: 0.060000 0.000000 0.060000 ( 0.054021); Time.at.in_time_zone: 0.120000 0.000000 0.120000 (0.115737) 【参考方案1】:

这个问题让我印象深刻的是,您关注的代码是从deserialize_from_cache(json) 内部调用的。为什么that 被这么频繁地调用?您能否进一步查看调用链,看看是否可以减少 json-to-time 解析的数量?

【讨论】:

以上是关于在 Rails 中使 TimeWithZone 对象变得惰性的主要内容,如果未能解决你的问题,请参考以下文章

默认情况下,如何在 Rails 中使 cookie 安全(仅限 https)?

如何在Rails中使数据库字段为只读?

在 Rails 3 中将 UTC 转换为本地时间

如何在 Ruby on Rails 迁移中使列唯一并为其编制索引?

如何在 ruby​​ on rails 中使每个 user_id 的列数据唯一

如何在数据库中获取原始的“created_at”值(不是转换为 ActiveSupport::TimeWithZone 的对象)