# Tuple indexing taking time?

**URL:** <https://discourse.julialang.org/t/tuple-indexing-taking-time/58309>\
**Category:** Performance\
**Created:** [March 31, 2021, 3:56pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309 "2021-03-31T15:56:43Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![markmbaum](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/markmbaum/32/32745_2.png) [@markmbaum](https://discourse.julialang.org/u/markmbaum)\
**Post date:** [March 31, 2021, 3:56pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/1 "2021-03-31T15:56:43Z")

</div>

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.

 ![Untitled](https://global.discourse-cdn.com/julialang/original/3X/7/0/70bae7be90bda4bf687058c071ed8f23e7ac4e06.png)

Zooming in, the little spike above the `getindex` block is `PyCall.jl, pydecref`, which I don’t know how to interpret.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [March 31, 2021, 4:02pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/2 "2021-03-31T16:02:23Z")

</div>

Are you using indexing with multiple values? E.g., `t[2:4]`? It’d be helpful if you could show a bit of your code.

---

<div class="post-metadata">

**Author:** ![markmbaum](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/markmbaum/32/32745_2.png) [@markmbaum](https://discourse.julialang.org/u/markmbaum)\
**Post date:** [March 31, 2021, 4:05pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/3 "2021-03-31T16:05:41Z")

</div>

The main red blocks point to the inside of the `for` loop in this function, where `σ += gas[i](T, P, idx)`:

```julia
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.

---

<div class="post-metadata">

**Author:** ![PetrKryslUCSD](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petrkryslucsd/32/215825_2.png) [@PetrKryslUCSD](https://discourse.julialang.org/u/PetrKryslUCSD)\
**Post date:** [March 31, 2021, 4:12pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/4 "2021-03-31T16:12:26Z")

</div>

`Real`s are abstract. Not good. Better:

```julia
function _schwarzschild(P, I, param)::Float64

```

---

<div class="post-metadata">

**Author:** ![markmbaum](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/markmbaum/32/32745_2.png) [@markmbaum](https://discourse.julialang.org/u/markmbaum)\
**Post date:** [March 31, 2021, 4:14pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/5 "2021-03-31T16:14:31Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![PetrKryslUCSD](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petrkryslucsd/32/215825_2.png) [@PetrKryslUCSD](https://discourse.julialang.org/u/PetrKryslUCSD)\
**Post date:** [March 31, 2021, 4:15pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/6 "2021-03-31T16:15:26Z")

</div>

Also: unpacking `param` may result in type instability, I think. Function barrier?

---

<div class="post-metadata">

**Author:** ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)\
**Post date:** [March 31, 2021, 4:16pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/7 "2021-03-31T16:16:18Z")

</div>

It’s argument annotation, so it is not very important (just some modest constraints on argument types).

I guess this tuple is a tuple of functions, and probably program take some run time to figure out, which function to run.

`@code_warntype` can help here.

---

<div class="post-metadata">

**Author:** ![markmbaum](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/markmbaum/32/32745_2.png) [@markmbaum](https://discourse.julialang.org/u/markmbaum)\
**Post date:** [March 31, 2021, 4:19pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/8 "2021-03-31T16:19:05Z")

</div>

Ok I’ll learn how to use `@code_warntype` and report back.

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [March 31, 2021, 4:50pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/9 "2021-03-31T16:50:40Z")

</div>

> [@PetrKryslUCSD](#):
>
> `Real` s are abstract. Not good. Better:

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.

---

<div class="post-metadata">

**Author:** ![PetrKryslUCSD](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petrkryslucsd/32/215825_2.png) [@PetrKryslUCSD](https://discourse.julialang.org/u/PetrKryslUCSD)\
**Post date:** [March 31, 2021, 5:22pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/10 "2021-03-31T17:22:01Z")

</div>

Right you are.

I think what tripped me up was this observation of mine from about a week ago:

```julia
julia> f(2.0)
-2.18504e+00

julia> f(2)
-2.18504e+00

julia> methods(f)
# 1 method for generic function "f":
[1] f(x::Real) in Main at REPL[5]:1

```

I noticed that specialized methods had not been derived for `f`. How do we explain that?

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [March 31, 2021, 5:27pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/11 "2021-03-31T17:27:20Z")

</div>

> [@PetrKryslUCSD](#):
>
> I noticed that specialized methods had not been derived for `f` . How do we explain that?

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 _call_ `f`, and you can see that in the `@code_warntype f(2)`

---

<div class="post-metadata">

**Author:** ![PetrKryslUCSD](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petrkryslucsd/32/215825_2.png) [@PetrKryslUCSD](https://discourse.julialang.org/u/PetrKryslUCSD)\
**Post date:** [March 31, 2021, 5:28pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/12 "2021-03-31T17:28:46Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [March 31, 2021, 5:31pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/13 "2021-03-31T17:31:16Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![markmbaum](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/markmbaum/32/32745_2.png) [@markmbaum](https://discourse.julialang.org/u/markmbaum)\
**Post date:** [March 31, 2021, 5:32pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/14 "2021-03-31T17:32:47Z")

</div>

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.

 ![Untitled](https://global.discourse-cdn.com/julialang/original/3X/8/f/8f955a6e2a232eeba374a88a556e98d233493e20.png)

I called the new type `GasPack`. There is still a little bit of empty space above that block though 🤷‍♀️.

---

<div class="post-metadata">

**Author:** ![PetrKryslUCSD](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petrkryslucsd/32/215825_2.png) [@PetrKryslUCSD](https://discourse.julialang.org/u/PetrKryslUCSD)\
**Post date:** [March 31, 2021, 5:34pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/15 "2021-03-31T17:34:30Z")

</div>

So would it be correct to say that `f(x::Real) = ...` was equivalent to `f(x::T) where {T<:Real} = ...`?

---

<div class="post-metadata">

**Author:** ![markmbaum](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/markmbaum/32/32745_2.png) [@markmbaum](https://discourse.julialang.org/u/markmbaum)\
**Post date:** [March 31, 2021, 5:39pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/16 "2021-03-31T17:39:12Z")

</div>

I don’t really understand how to decipher the `@code_warntype` results though. This is the first bit

```julia
julia> @code_warntype _schwarzschild(1e5, 1, ((CO2,H2O), 10, 667.0, 9.8, 0.03, M))
Variables
  #self#::Core.Compiler.Const(_schwarzschild, false)
  P@_2::Float64
  I::Int64
  param::Tuple{Tuple{MinorGas,VariableGas{var"#1#2"}},Int64,Float64,Float64,Float64,MoistAdiabat}
  @_5::Int64
  gas::Tuple{MinorGas,VariableGas{var"#1#2"}}
  idx::Int64
  ν::Float64
  g::Float64
  μ::Float64
  Tfun::MoistAdiabat
  T::Float64
  σ::Float64
  @_14::Union{Nothing, Tuple{Int64,Int64}}
  i::Int64
  P@_16::Float64

```

what is `@_14`?

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [March 31, 2021, 5:46pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/17 "2021-03-31T17:46:25Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [March 31, 2021, 5:46pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/18 "2021-03-31T17:46:54Z")

</div>

> [@markmbaum](#):
>
> The `gas` tuple does have elements with different types, though.

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`.

I wonder if [GitHub - cstjean/Unrolled.jl: Unrolling loops at compile-time](https://github.com/cstjean/Unrolled.jl) would help here?

---

<div class="post-metadata">

**Author:** ![markmbaum](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/markmbaum/32/32745_2.png) [@markmbaum](https://discourse.julialang.org/u/markmbaum)\
**Post date:** [March 31, 2021, 5:47pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/19 "2021-03-31T17:47:58Z")

</div>

That seems right.

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`.

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [March 31, 2021, 5:57pm UTC](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309/20 "2021-03-31T17:57:46Z")

</div>

I think so. They only stop being equivalent when you have them inside type parameters (which are invariant):

```julia
julia> struct Wrapper{T}; a :: T; end

julia> f(x :: Wrapper{Real}) = x.a
f (generic function with 1 method)

julia> g(x :: Wrapper{T}) where {T<:Real} = x.a
g (generic function with 1 method)

julia> x1 = Wrapper{Real}(1.0)
Wrapper{Real}(1.0)

julia> x2 = Wrapper{Float64}(1.0)
Wrapper{Float64}(1.0)

julia> f(x1)
1.0

julia> f(x2)
ERROR: MethodError: no method matching f(::Wrapper{Float64})
Closest candidates are:
  f(::Wrapper{Real}) at REPL[2]:1
Stacktrace:
 [1] top-level scope at REPL[7]:1

julia> g(x1)
1.0

julia> g(x2)
1.0

```

In this case the short way to write `g` would be:

```julia
julia> h(x :: Wrapper{<:Real}) = x.a
h (generic function with 1 method)

julia> h(x1)
1.0

julia> h(x2)
1.0

```

[Next page](https://discourse.julialang.org/t/tuple-indexing-taking-time/58309.md?page=2)
