【问题标题】:ruby string literals cached?ruby 字符串文字缓存?
【发布时间】:2016-08-31 04:07:15
【问题描述】:

我有这种形式的代码:

class String
  def is_jpg?
    start_with? "\xFF\xD8\xFF".b
  end
end

String#b 返回一个字符串的副本,但编码为 ASCII-8BIT。我的问题

ruby 解释器是否足够聪明,可以缓存上例中 b() 的结果,以便为不同的 String 实例调用此方法不必为每个实例创建一个新的 "\xFF\xD8\xFF" 副本时间?或者我应该这样做:

class String
  JPG_SIGNATURE = "\xFF\xD8\xFF".b
  def is_jpg?
    start_with? JPG_SIGNATURE
  end
end

答案是否取决于 ruby​​ 版本和/或解释器?我正在使用 MRI 2.2.x 即将成为 2.3.x。

【问题讨论】:

  • 您不能将b 方法的结果保存为常量吗?
  • @davidhu2000 这就是他在上个sn-p中所做的。
  • 顺便说一句,您不应该使用is_ 前缀。该方法应该被称为jpg?

标签: ruby string


【解决方案1】:

不,默认情况下 Ruby 会创建 String 的新副本,但您可以通过冻结 String 来防止这种情况发生

class String
  def is_jpg?
    start_with? "\xFF\xD8\xFF".freeze.b
  end
end

class String
  JPG_SIGNATURE = "\xFF\xD8\xFF".freeze.b
  def is_jpg?
    start_with? JPG_SIGNATURE
  end
end

您可以在本文中了解更多相关信息。 http://blog.honeybadger.io/when-to-use-freeze-and-frozen-in-ruby/

【讨论】:

  • 太棒了。不知道冻结也可以用于此。
【解决方案2】:

回答你的问题:

ruby 解释器是否足够聪明,可以缓存 b() 的结果

没有。这违背了 Ruby 的核心概念。像这样的字符串文字每次都会被视为新对象。

def foo
  x = 'foo'
  puts x.object_id
end

> foo
70281000268840
> foo
70281000264340

您的第二种选择是执行此操作的更好方法。常量应该定义一次。它会在内存中保存一个空间。

你也可以用String::JPG_SIGNATURE = "\xFF\xD8\xFF".b来定义

编辑:阅读kcdragon's answer。他建议freeze 处理它,同样的基准数真的很漂亮!

【讨论】:

  • 当然,如果你分配给一个变量它不能缓存结果,因为重用对象 ID 会与语言模型不一致。但它可以缓存在 OP 的第一个示例中而不会造成伤害。我猜这是依赖于 Ruby-VM 的。
  • 如果我分配给一个变量(而不是一个常量),它的范围被限制在该方法内,并且在每次调用方法时,一个新的空间/新对象将出现,即使在第一个OP的例子。
【解决方案3】:

我对三种实现进行了基准测试:没有优化的正常、使用 const 和使用冻结字符串。

            user     system      total        real
normal  0.270000   0.000000   0.270000 (  0.272052)
const   0.160000   0.000000   0.160000 (  0.155427)
freeze  0.240000   0.000000   0.240000 (  0.238170)

似乎在我使用官方 Ruby VM 的机器上,字符串没有被缓存,因为使用 const 确实提供了速度提升。冻结字符串也可以提供提升,但要小得多。

请注意,我必须调用该方法 数百万 次才能看到任何明显的差异。除非您的分析表明这种特定方法是一个严重的瓶颈,否则我会将其归类为过早优化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-17
    • 1970-01-01
    • 1970-01-01
    • 2018-09-12
    • 1970-01-01
    相关资源
    最近更新 更多