单例数组的魔力从何而来?

phi*_*ler 7 arrays runtime julia

Vector{Missing}和之间的以下差异Vector{Int}让我感到惊讶(以积极的方式):

julia> @btime fill(20, 10^7);
  15.980 ms (2 allocations: 76.29 MiB)

julia> @btime fill(missing, 10^7);
  20.603 ns (1 allocation: 80 bytes)

julia> Base.summarysize(fill(20, 10^7))
80000040

julia> Base.summarysize(fill(missing, 10^7))
40

julia> typeof(fill(20, 10^7))
Vector{Int64} (alias for Array{Int64, 1})

julia> typeof(fill(missing, 10^7))
Vector{Missing} (alias for Array{Missing, 1})
Run Code Online (Sandbox Code Playgroud)

鉴于这fill(missing, n)不会产生像 那样的优化结构FillArray,那么这种对单例的优化是如何实现的呢?我猜想它会以某种方式自动从单例大小为零的事实中消失,但是如何呢?

我尝试阅读array.cjulia.h,但无法真正理解详细信息。也许真正的问题是运行时系统如何处理单例......?

Osc*_*ith 7

基本答案是a = Array(T)Julia 总是分配sizeof(T)*length(a)+40字节。由于sizeof(Missing) == 0,这意味着它不会分配与长度相关的任何字节。

索引发生在 的第 569 行array.c,相关行是

jl_value_t *r = undefref_check((jl_datatype_t*)eltype, jl_new_bits(eltype, &((char*)a->data)[i * a->elsize]))
Run Code Online (Sandbox Code Playgroud)

当数组大小为零时,a->data[i * a->elsize]引用 的开头a->data。然而,它的内容是完全无关的:当eltypehas 0时jl_datatype_sizejl_new_bits 忽略数据指针,而是instance从 中返回字段eltype,这是单例对象。

  • 啊,这是特别的地方:https://github.com/JuliaLang/julia/blob/63f62943e13d47c917e4136bc13c062a2b09fbe1/src/datatype.c#L808。`jl_new_bits` 忽略空指针 `data` 并返回存储在单例的 `jl_datatype_t` 中的实例。 (4认同)
  • 您可能可以使用该信息添加另一个答案,或者建议编辑奥斯卡的答案以包含该信息,因为它是您想知道的一部分。(对 SO 的评论有些短暂。) (3认同)
  • 我也是这么想的,但是索引是如何工作的呢?分配块的开头(长度为零的数据部分之后)是否有一个神奇的值来告诉调用“我是空的,但在这里查找单例”? (2认同)