A fairly general question about a function that I’m working on before I try to summarize what it’s doing. The profile view is below. There are three main red blocks that take up most of the time, each for the same line of code, which makes sense. But, I don’t understand why there is so much tuple getindex time above these blocks or why there is also empty time above them. Is this telling me that I have a dynamic portion that needs to be fixed? The rest of the operations aren’t trivial, so I don’t have any reason to expect tuple indexing to be a slow part.
The main red blocks point to the inside of the for loop in this function, where σ += gas[i](T, P, idx):
function _schwarzschild(P::Real, I::Real, param)::Float64
#unpack
gas, idx, ν, g, μ, Tfun = param
#unravel the log pressure coordinate
P = exp(-P)
#compute temperature from given function
T = Tfun(P)
#get concentration-scaled cross-section from interpolators
σ = 0.0
for i = 1:length(gas)
σ += gas[i](T, P, idx)
end
#compute dI/dlogP
P*schwarzschild(I, ν, σ, g, μ, T)
end
the gas variable is a Tuple but I don’t know why indexing it would take roughly as much time as the operation that each element performs, which is a 2D interpolation. The gas tuple does have elements with different types, though.
Does that matter though? My “P::Real, I::Real” in the definition is just supposed to constrain input types, even though P and T are always Float64 in this case.
No, the use of ::Real in a function argument type has no performance penalty whatsoever. Writing function _schwarzschild(P::Real, I::Real, ...). is perfectly fine.
That’s not what methods shows anyway–it just tells you that there is a single method for f, and that method accepts any subtype of ::Real. The actual specialization for a particular type happens when you callf, and you can see that in the @code_warntype f(2)
Didn’t it used to be that I could see those methods when the function was called for different arguments? I think I put an example like this into my tutorial a while back.
Edit: Now I remember, I have to type the argument explicitly.
Not as far as I know (and I just checked that it doesn’t happen in versions as old as Julia 0.5). Maybe you actually defined foo(x::Real) and foo(x::Int) separately in your tutorial?
So, one solution seems to be replacing that gas tuple with a new type. The tuple has different types of objects in it. If I replace the tuple with a single type that explicitly organizes the different gasses, then use a new function on it instead of the for loop, the getindex blocks go away.
I think that’s a temp variable found somewhere in the body below. Could it be that MinorGas is not a completely concrete type? A tuple should work as well as a struct with two entries in terms of compilation and type inference, but if your gas types are abstract or the function you run when calling them is not completely specified by the type, then that’s a type instability
I think this is the basic problem. Indexing into a tuple with different types is generally not type-stable because the type of the result depends on the value of i.
It also speeds up if, instead of a tuple, the gas argument is just a Vector{AbstractGas}, where I’ve defined the function for AbstractGas that is called inside the for loop of _schwarzschild.