JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.05k stars 5.43k forks source link

Broadcasting getindex with key over tuple of inhomogeneous NamedTuples is allocating #51617

Open wsshin opened 9 months ago

wsshin commented 9 months ago

Consider a tuple of NamedTuples of f::Float64 and i::Int64 (inhomogeneous types):

julia> VERSION
v"1.9.3"

julia> s = ((f = 0.0, i = 128), (f = 1.0, i = 256))
((f = 0.0, i = 128), (f = 1.0, i = 256))

Constructing a tuple with the values of f by broadcasting getindex with the key :f is allocating and slow:

julia> @btime getindex.($s, :f)
  54.215 ns (3 allocations: 64 bytes)
(0.0, 1.0)

On the other hand, doing the same thing by broadcasting getindex with the index 1 (corresponding to the key :f) is non-allocating and fast:

julia> @btime getindex.($s, 1)
  2.701 ns (0 allocations: 0 bytes)
(0.0, 1.0)

The allocation seems to happen only for NamedTuples composed of inhomogeneous types. For example, a similar case with a homogeneous type such as

julia> s = ((f1 = 0.0, f2 = 128.0), (f1 = 1.0, f2 = 256.0))
((f1 = 0.0, f2 = 128.0), (f1 = 1.0, f2 = 256.0))

where both fields of the NamedTuple are Float64, does not allocate even if getindex is used with a key:

julia> @btime getindex.($s, :f1)
  2.777 ns (0 allocations: 0 bytes)
(0.0, 1.0)
mikmoore commented 9 months ago

Related #43333. A workaround (in the meantime) is to define a new function getf(x) = getproperty(x,:f) and broadcast that instead of getproperty/getfield/getindex directly, although this is a bit more tedious.

wsshin commented 9 months ago

Thanks, @mikmoore, I will look into the referenced issue to see if I can fix it. In the meantime, I guess I can use (x->getindex(x, :f)).(s) following workaround.