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

Where does the magic of singleton arrays come from?

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})

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

我尝试阅读 array.c and julia.h,但无法真正理解其中的细节。也许真正的问题是 运行时系统如何处理单例...?

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

索引发生在line 569 of array.c,相关行是

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

当数组大小为零时,a->data[i * a->elsize]指的是a->data的开头。然而,它的内容是完全不相关的:当 eltype 有零时 jl_datatype_sizejl_new_bits ignores the data pointer 而不是 returns 来自的 instance 字段eltype,这是单例对象。