# Unused \`where T\` causes a function to become very slow

**URL:** <https://discourse.julialang.org/t/unused-where-t-causes-a-function-to-become-very-slow/39727>\
**Category:** Performance\
**Tags:** question\
**Created:** [May 18, 2020, 11:52pm UTC](https://discourse.julialang.org/t/unused-where-t-causes-a-function-to-become-very-slow/39727 "2020-05-18T23:52:51Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![mabokhamis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mabokhamis/32/7282_2.png) [@mabokhamis](https://discourse.julialang.org/u/mabokhamis)\
**Post date:** [May 18, 2020, 11:52pm UTC](https://discourse.julialang.org/t/unused-where-t-causes-a-function-to-become-very-slow/39727/1 "2020-05-18T23:52:51Z")

</div>

We noticed that an unused `where T` in a function declaration can cause the function to become much slower and to allocate a lot more memory. Consider for example the following two functions which are identical except that the second one has an unnecessary `where T`:

```julia
function f1(x::Int)
    return mod(2 * x + 1, 1000)
end
function f2(x::Int) where T # Note the unused `where T`
    return mod(2 * x + 1, 1000)
end

```

Comparing performance of the two functions reveals that the second is much slower:

```julia
function test(f::Function)
    x = 0
    for i = 1:100000000
        x = f(x)
    end
    x
end
test(f1)
@time test(f1) # 0.389844 seconds
test(f2)
@time test(f2) # 2.278735 seconds (96.00 M allocations: 1.431 GiB, 0.51% gc time)

```

Is this a bug? Is it possible to add an optimization to Julia to ignore unused `where` arguments so that they don’t impact performance anymore? Such unused arguments can sometimes occur in large Julia codebases as leftovers from previous reversions, etc. Another potential solution is to treat such unused arguments as syntax errors…

---

<div class="post-metadata">

**Author:** ![mabokhamis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mabokhamis/32/7282_2.png) [@mabokhamis](https://discourse.julialang.org/u/mabokhamis)\
**Post date:** [May 18, 2020, 11:59pm UTC](https://discourse.julialang.org/t/unused-where-t-causes-a-function-to-become-very-slow/39727/2 "2020-05-18T23:59:15Z")

</div>

This can be reproduced on Julia `1.4.0`. Here is my `versioninfo()`:

```julia
julia> versioninfo()
Julia Version 1.4.0
Commit b8e9a9ecc6 (2020-03-21 16:36 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-8.0.1 (ORCJIT, skylake)

```

---

<div class="post-metadata">

**Author:** ![NHDaly](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nhdaly/32/1413_2.png) [@NHDaly](https://discourse.julialang.org/u/NHDaly)\
**Post date:** [May 19, 2020, 12:04am UTC](https://discourse.julialang.org/t/unused-where-t-causes-a-function-to-become-very-slow/39727/3 "2020-05-19T00:04:21Z")

</div>

Wow, that’s wild! I would’ve guessed that would be a parse error, yeah.

I can also reproduce it on 1.5, on `Commit c3d6a463be* (12 days old master)`.

---

<div class="post-metadata">

**Author:** ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)\
**Post date:** [May 19, 2020, 12:06am UTC](https://discourse.julialang.org/t/unused-where-t-causes-a-function-to-become-very-slow/39727/4 "2020-05-19T00:06:03Z")

</div>

Note that you can do this, to check your package for such problems:

```julia
module UnTee
    function f1(x::Int)
        return mod(2 * x + 1, 1000)
    end
    function f2(x::Int) where T # Note the unused `where T`
        return mod(2 * x + 1, 1000)
    end
end

using Test
@test isempty(detect_unbound_args(UnTee)) # fails

```

---

<div class="post-metadata">

**Author:** ![NHDaly](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nhdaly/32/1413_2.png) [@NHDaly](https://discourse.julialang.org/u/NHDaly)\
**Post date:** [May 19, 2020, 12:31am UTC](https://discourse.julialang.org/t/unused-where-t-causes-a-function-to-become-very-slow/39727/5 "2020-05-19T00:31:13Z")

</div>

Is there any reason why anyone _would_ ever want unbound type parameters? Can this just be a parse error? Can they be used for anything?

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [May 19, 2020, 12:46am UTC](https://discourse.julialang.org/t/unused-where-t-causes-a-function-to-become-very-slow/39727/6 "2020-05-19T00:46:41Z")

</div>

Making things even more interesting, `f1` and `f2` have identical code\_lowered, and code\_typed. There are some differences in the code\_native, but that’s above my pay grade.

---

<div class="post-metadata">

**Author:** ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)\
**Post date:** [May 19, 2020, 4:23am UTC](https://discourse.julialang.org/t/unused-where-t-causes-a-function-to-become-very-slow/39727/7 "2020-05-19T04:23:18Z")

</div>

I’m sure this has come up before, but I can’t for my life find an issue or discussion about it. It feels like it warrants an issue though 🙂

---

<div class="post-metadata">

**Author:** ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)\
**Post date:** [May 19, 2020, 5:10am UTC](https://discourse.julialang.org/t/unused-where-t-causes-a-function-to-become-very-slow/39727/8 "2020-05-19T05:10:08Z")

</div>

> [@Oscar\_Smith](#):
>
> Making things even more interesting, `f1` and `f2` have identical code\_lowered, and code\_typed.

You would have to look at `test(f1)` vs `test(f2)`.

---

<div class="post-metadata">

**Author:** ![mabokhamis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mabokhamis/32/7282_2.png) [@mabokhamis](https://discourse.julialang.org/u/mabokhamis)\
**Post date:** [May 19, 2020, 5:57am UTC](https://discourse.julialang.org/t/unused-where-t-causes-a-function-to-become-very-slow/39727/9 "2020-05-19T05:57:43Z")

</div>

Thanks all! I filed this as a github issue [Unused `where T` causes a function to become very slow · Issue #35935 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/35935) and linked to this discussion

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [May 19, 2020, 7:42am UTC](https://discourse.julialang.org/t/unused-where-t-causes-a-function-to-become-very-slow/39727/10 "2020-05-19T07:42:21Z")

</div>

Unfortunately, this has a lot of false positive, preventing me from adding it to my test suites.

But extremely useful – I found a few!  
But, this remains:

```julia
julia> using VectorizationBase, Test
[Info: Precompiling VectorizationBase [3d5dd08c-fd9d-11e8-17fa-ed2836048c2f]

julia> detect_unbound_args(VectorizationBase)
[1] gep(ptr::Ptr{T}, i::Tuple{Vararg{VecElement{I},W}}) where {W, T, I<:Integer} in VectorizationBase at /home/chriselrod/.julia/dev/VectorizationBase/src/vectorizable.jl:202
[2] vbroadcast(::Type{Tuple{Vararg{VecElement{T},W}}}, ptr::Ptr) where {W, T} in VectorizationBase at /home/chriselrod/.julia/dev/VectorizationBase/src/VectorizationBase.jl:140
[3] vbroadcast(::Type{Tuple{Vararg{VecElement{T},W}}}, v::Tuple{Vararg{VecElement{T},W}}) where {W, T} in VectorizationBase at /home/chriselrod/.julia/dev/VectorizationBase/src/VectorizationBase.jl:142
[4] vbroadcast(::Type{Tuple{Vararg{VecElement{T1},W}}}, s::T2) where {W, T1<:Integer, T2<:Integer} in VectorizationBase at /home/chriselrod/.julia/dev/VectorizationBase/src/VectorizationBase.jl:139
[5] vzero(::Type{Tuple{Vararg{VecElement{T},W}}}) where {W, T} in VectorizationBase at /home/chriselrod/.julia/dev/VectorizationBase/src/VectorizationBase.jl:117
[6] vbroadcast(::Type{Tuple{Vararg{VecElement{T1},W}}}, s) where {W, T1} in VectorizationBase at /home/chriselrod/.julia/dev/VectorizationBase/src/VectorizationBase.jl:138
[7] vone(::Type{Tuple{Vararg{VecElement{T},W}}}) where {W, T} in VectorizationBase at /home/chriselrod/.julia/dev/VectorizationBase/src/VectorizationBase.jl:146

```

Lines 138-146, as an example:

```julia
@inline vbroadcast(::Type{Vec{W,T1}}, s) where {W,T1} = vbroadcast(Vec{W,T1}, convert(T1,s))
@inline vbroadcast(::Type{Vec{W,T1}}, s::T2) where {W,T1<:Integer,T2<:Integer} = vbroadcast(Vec{W,T1}, s % T1)
@inline vbroadcast(::Type{Vec{W,T}}, ptr::Ptr) where {W,T} = vbroadcast(Vec{W,T}, Base.unsafe_convert(Ptr{T},ptr))
@inline vbroadcast(::Type{SVec{W,T}}, s) where {W,T} = SVec(vbroadcast(Vec{W,T}, s))
@inline vbroadcast(::Type{Vec{W,T}}, v::Vec{W,T}) where {W,T} = v
@inline vbroadcast(::Type{SVec{W,T}}, v::SVec{W,T}) where {W,T} = v
@inline vbroadcast(::Type{SVec{W,T}}, v::Vec{W,T}) where {W,T} = SVec(v)

@inline vone(::Type{Vec{W,T}}) where {W,T} = vbroadcast(Vec{W,T}, one(T))

```

I really wish this were a parser error. Once upon a time it was rendering this issue harmless, but now it is rather nasty.

---

<div class="post-metadata">

**Author:** ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)\
**Post date:** [May 19, 2020, 9:20am UTC](https://discourse.julialang.org/t/unused-where-t-causes-a-function-to-become-very-slow/39727/11 "2020-05-19T09:20:21Z")

</div>

Oh I hadn’t noticed false positives yet, I only recently found this tool. I guess they count as bugs in Test?

---

<div class="post-metadata">

**Author:** ![Per](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/per/32/10387_2.png) [@Per](https://discourse.julialang.org/u/Per)\
**Post date:** [May 19, 2020, 11:39am UTC](https://discourse.julialang.org/t/unused-where-t-causes-a-function-to-become-very-slow/39727/12 "2020-05-19T11:39:24Z")

</div>

I think the “false positives” are due to `Vararg{T}` matching zero arguments, resulting in `T` being unbound.

To capture an element type in the call signature, one can use something like:

```julia
f(::Tuple{T, Vararg{T}}) where {T}

```

(The empty tuple must ~~then~~ be handled by a separate method that does not depend on T.)

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [May 19, 2020, 5:07pm UTC](https://discourse.julialang.org/t/unused-where-t-causes-a-function-to-become-very-slow/39727/13 "2020-05-19T17:07:41Z")

</div>

Interesting!

```julia
julia> const _Vec{W,T} = Tuple{Core.VecElement{T},Vararg{Core.VecElement{T},W}}
Tuple{VecElement{T},Vararg{VecElement{T},W}} where T where W

julia> using VectorizationBase: Vec

julia> Vec{8,Float64} === _Vec{7,Float64}
true

```

Also interesting that the problem seems to be solved by wrapping the tuple in a struct (which I do anyway so that I can overload methods):

```julia
julia> NTuple{0,Float64} === NTuple{0,Int}
true

julia> SVec{0,Float64} === SVec{0,Int}
false

julia> dump(SVec{2,Float64}(1.0)) # Just a tuple-wrapper
SVec{2,Float64}
  data: Tuple{VecElement{Float64},VecElement{Float64}}
    1: VecElement{Float64}
      value: Float64 1.0
    2: VecElement{Float64}
      value: Float64 1.0

```

Meaning that I should be able to continue to provide `SVec` as the primary API and `Vec` as the secondary, while dealing with the awkward `_Vec` internally.

EDIT:  
Now…

```julia
julia -O0 -E "using VectorizationBase, Test; detect_unbound_args(VectorizationBase)"
Method[]

```

Great, looks like I can start testing for this!
