首页 > 解决方案 > 为什么不保留空数组和哈希?

问题描述

最近我发现 Ruby 没有优化[]并被{}实习以指向一个公共的共享对象。演示:

irb(main):001:0> [].object_id
=> 70284401361960
irb(main):002:0> [].object_id
=> 70284392762340 # different
irb(main):003:0> [].object_id
=> 70284124310100 # different

irb(main):005:0> {}.object_id
=> 70284392857480
irb(main):006:0> {}.object_id
=> 70284392870480 # different
irb(main):007:0> {}.object_id
=> 70284392904360 # different

我知道通常使用空哈希和数组文字来初始化将立即变异的值。但是,即使您这样做[].freeze.object_id{}.freeze.object_id相反,也会发生这种情况。

String当 env varRUBYOPT设置为时,将此与 进行对比--enable-frozen-string-literal

irb(main):001:0> ""
=> 70284400947400
irb(main):002:0> ""
=> 70284400947400 # same
irb(main):003:0> ""
=> 70284400947400 # same

即使您不启用冻结字符串文字,如果您"".freeze.object_id改为调用,每次都会获得相同的对象 id,尽管我怀疑初始""文字仍然分配了一个freeze正在调用的中间字符串对象。

在对性能敏感的代码库中(好吧,作为对性能敏感的你可以让自己在仍然使用 MRI 的同时保持哈哈),我已经看到了这种解决方法,它使用以下共享常量代替[]{}用于哈希或数组不的情况t 需要是可变的:

module LessAllocations
  EMPTY_HASH = {}.freeze
  EMPTY_ARRAY = [].freeze

  # String literals are already frozen, use '' instead
  # EMPTY_STRING = ''
end

所以我的问题是:

  1. 这是错失的优化机会吗?

    似乎可以将窥视孔优化写入实习生冻结的空哈希或数组文字。需要成为语言语义的一部分,即是否存在任何可观察到的差异(显然除了 的行为object_id)?

  2. 空散列和数组字面量是否由标记指针表示?他们甚至会导致任何分配发生吗?

标签: arraysrubyoptimizationlanguage-lawyer

解决方案


这是错失的优化机会吗?

要回答这个问题,必须对现实世界的内存使用情况进行调查,但我认为这不太可能。你可以做一个小实验...

class Array
  EMPTY_ARRAY = [].freeze
  
  def freeze
    empty? ? EMPTY_ARRAY : super
  end
end

空物体非常小。与您的程序使用内存的其他所有内容相比,拥有如此多的内存以致于它们使用大量内存是一种极端情况。

我知道通常使用空哈希和数组文字来初始化将立即变异的值。

出于这个原因,将写入时复制添加到空哈希和数组可能会减慢速度。

但即使您改为使用 [].freeze.object_id 或 {}.freeze.object_id 也会发生这种情况。

冻结它们意味着您提前知道它们将保持空置状态,这是极其罕见的。拥有这么多已知的空哈希和数组以致成为性能问题是一种极端情况。不断的变通方法似乎很好。


推荐阅读