最近我发现 Ruby 没有优化 []
和 {}
以驻留在指向一个公共(public)共享对象。演示:
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
对比,当环境变量 RUBYOPT
设置为 --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 的同时大声笑)我已经看到了这个解决方法,它使用以下共享常量而不是 []
或 {}
对于散列或数组不需要可变的情况:
module LessAllocations
EMPTY_HASH = {}.freeze
EMPTY_ARRAY = [].freeze
# String literals are already frozen, use '' instead
# EMPTY_STRING = ''
end
所以我的问题是:
这是错失的优化机会吗?
似乎可以将窥孔优化写入实习生卡住的空哈希或数组文字。需要成为语言语义的一部分,即是否存在任何可观察到的差异(显然除了
object_id
的行为之外)?空哈希和数组文字是否由标记指针表示?它们甚至会导致任何分配发生吗?
最佳答案
Is this a missed optimization opportunity?
要回答这个问题,必须对现实世界的内存使用情况进行调查,但我认为这不太可能。你可以做个小实验...
class Array
EMPTY_ARRAY = [].freeze
def freeze
empty? ? EMPTY_ARRAY : super
end
end
空对象非常小。与您的程序使用内存的其他所有内容相比,它们使用大量内存是一种边缘情况。
I understand that often empty hashes and array literals are used to initialize values that will be immediately mutated.
因此,向空哈希和数组添加写时复制可能会降低速度。
But this happens even if you do [].freeze.object_id or {}.freeze.object_id instead.
卡住它们意味着您提前知道它们将保持空状态,这种情况极为罕见。有这么多已知的空哈希和数组成为性能问题是一种边缘情况。不断的解决方法似乎很好。
关于arrays - 为什么不保留空数组和哈希值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66393640/