# Dramatic performance change by adding additional unused method

**URL:** <https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771>\
**Category:** Performance\
**Created:** [November 19, 2021, 11:56am UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771 "2021-11-19T11:56:03Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![peremato](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/peremato/32/29128_2.png) [@peremato](https://discourse.julialang.org/u/peremato)\
**Post date:** [November 19, 2021, 11:56am UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/1 "2021-11-19T11:56:03Z")

</div>

This is a followup of [not-understanding-linear-growing-allocations](https://discourse.julialang.org/t/not-understanding-linear-growing-allocations/71734). It took me a while to get a manageable reproducer after my first attempt of forgetting a simple `const` in a type alias. The code is as follows:

```julia
using BenchmarkTools
using StaticArrays

const Vector3 = SVector{3,Float64}

abstract type AbstractShape end

struct Box <: AbstractShape
    x::Float64; y::Float64; z::Float64
end
function extent(b::Box)
    (Vector3(-b.x,-b.y,-b.z), Vector3(b.x,b.y,b.z))
end

struct SBox <: AbstractShape
    x::Float64; y::Float64; z::Float64
end
function extent(b::SBox)
    (Vector3(-b.x,-b.y,-b.z), Vector3(b.x,b.y,b.z))
end

struct Circle <: AbstractShape
    r::Float64
end
function extent(c::Circle)
    (Vector3(-c.r,-c.r,-c.r), Vector3(c.r,c.r,c.r))
end

struct SCircle <: AbstractShape
    r::Float64
end
function extent(c::SCircle)
    (Vector3(-c.r,-c.r,-c.r), Vector3(c.r,c.r,c.r))
end

struct Figure
    label::String
    shape::AbstractShape
end

figure = Figure("1", Box(1,1,1))

function area(fig::Figure)
    lower, upper = extent(fig.shape)
    sum = 0.
    for i in 1:1000
        sum += (upper[1]-lower[1]) * (upper[2]-lower[2])
    end
    sum
end

```

Running `@time area(figure)` you get 8k allocations. With `@code_warntype` you can see that the problem is with `Main.extent(%1)::Any` returning `Any` instead of returning `Tuple{Vector3,Vector3}`. `@trace from Traceur.jl` didn’t help me.

To solve the problem you just need to remove (or comment) one of the overloads of `extent`. For example:

```julia
struct SCircle <: AbstractShape
    r::Float64
end
#function extent(c::SCircle)
# (Vector3(-c.r,-c.r,-c.r), Vector3(c.r,c.r,c.r))
#end

```

You then get

```julia
julia> @time area(figure)
  0.000002 seconds
4000.0

```

The question is, why is this? If this a bug? is this a stupid thing that I have done?  
Thanks very much for your help.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [November 19, 2021, 12:18pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/2 "2021-11-19T12:18:55Z")

</div>

Try using

> [@peremato](#):
>
> ```julia
> struct Figure{S} where S <: AbstractShape
> label::String
> shape::S
> end
> 
> ```

Having abstract fields is always a performance problem.

(I know that even if this solves the problem it does nor answer your doubt exactly. It may have to do with the fact that with the abstract type the function `area` cannot specialize)

---

<div class="post-metadata">

**Author:** ![peremato](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/peremato/32/29128_2.png) [@peremato](https://discourse.julialang.org/u/peremato)\
**Post date:** [November 19, 2021, 12:22pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/3 "2021-11-19T12:22:29Z")

</div>

This is not possible. I need polymorphism. Perhaps I could do it with `Any`. But in any case, why it works for 3 overloads and not for 4.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [November 19, 2021, 12:25pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/4 "2021-11-19T12:25:39Z")

</div>

> [@peremato](#):
>
> ` lower:: Vector3, upper:: Vector3 = extent(fig.shape)`

You could annotate the output here (as shown).

(Though I’m not sure if the previous suggestion is incompatible with what you need).

> [@peremato](#):
>
> But in any case, why it works for 3 overloads and not for 4.

There is a limit up to which the compiler does union splitting automatically, and above which it will just fall back to dynamic dispatch.

---

<div class="post-metadata">

**Author:** ![fredrikekre](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fredrikekre/32/1688_2.png) [@fredrikekre](https://discourse.julialang.org/u/fredrikekre)\
**Post date:** [November 19, 2021, 12:40pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/5 "2021-11-19T12:40:27Z")

</div>

> [@peremato](#):
>
> But in any case, why it works for 3 overloads and not for 4.

> <https://github.com/JuliaLang/julia/blob/96902377bde0b3e84c1319e0d5ad55039610d5ef/base/compiler/types.jl#L69>

---

<div class="post-metadata">

**Author:** ![carstenbauer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/carstenbauer/32/4981_2.png) [@carstenbauer](https://discourse.julialang.org/u/carstenbauer)\
**Post date:** [November 19, 2021, 12:54pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/6 "2021-11-19T12:54:03Z")

</div>

> [@peremato](#):
>
> This is not possible. I need polymorphism.

Could you elaborate a bit more on why you need this? It isn’t clear from your example since that would work perfectly fine with what @lmiq suggested. Are you changing the shape of a figure dynamically (i.e. assign `figure.shape` to a different shape)? Or is it just that you don’t know upfront which shape you will need?

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [November 19, 2021, 1:11pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/7 "2021-11-19T13:11:42Z")

</div>

```julia
struct Figure{S} where S <: AbstractShape
    label::String
    shape::S
end

```

This is a syntax error for me. Perhaps you meant

```julia
struct Figure{S<:AbstractShape}
    label::String
    shape::S
end

```

?

> [@peremato](#):
>
> This is not possible. I need polymorphism.

But this does give you [polymorphism](https://docs.julialang.org/en/v1/manual/types/#Parametric-Types). You can’t mutate it at runtime, but you cannot do that anyway with `struct`, you would need `mutable struct` for that.

( **Edit:** Defining the following method gives you a convenient constructor:

```julia
Figure(label, shape::T) where {T} = Figure{T}(label, shape)

```

)

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [November 19, 2021, 1:23pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/8 "2021-11-19T13:23:16Z")

</div>

> [@peremato](#):
>
> ```julia
> struct Box <: AbstractShape
> x::Float64; y::Float64; z::Float64
> end
> 
> ```

these could have been:

```julia
struct Box{T} <: AbstractShape
    x::T; y::T; z::T
end

```

and then this really should be:

```julia
struct Figure{T<:AbstractShape} where 
    label::String
    shape::T
end

```

if you don’t parameterize it, you’re forcing it to be (possibly) slow by forcing abstract type boxing

---

<div class="post-metadata">

**Author:** ![peremato](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/peremato/32/29128_2.png) [@peremato](https://discourse.julialang.org/u/peremato)\
**Post date:** [November 19, 2021, 1:48pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/9 "2021-11-19T13:48:51Z")

</div>

> [@lmiq](#):
>
> There is a limit up to which the compiler does union splitting automatically, and above which it will just fall back to dynamic dispatch.

This is a surprise to me. I do not mind if there is dynamic dispatch, I was actually expecting it (equivalent in C++ to virtual call) but what I was not expecting that it is allocating memory each time is dispatching.

---

<div class="post-metadata">

**Author:** ![peremato](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/peremato/32/29128_2.png) [@peremato](https://discourse.julialang.org/u/peremato)\
**Post date:** [November 19, 2021, 1:55pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/10 "2021-11-19T13:55:42Z")

</div>

> [@carstenbauer](#):
>
> Could you elaborate a bit more on why you need this? It isn’t clear from your example since that would work perfectly fine with what @leandromartinez98 suggested. Are you changing the shape of a figure dynamically (i.e. assign `figure.shape` to a different shape)? Or is it just that you don’t know upfront which shape you will need?

I working a prototype geometry modeler (see [GitHub - peremato/Geom4hep](https://github.com/peremato/Geom4hep)). I’ll be dealing with collections of nested structures of the equivalent `Figure` in the reproducer. `Figure` has to be generic but it has a `Shape` that is concretized with the different types of shapes. I am not changing the shape dynamically, Once I create a `Figure` is inmutable. Is this helping to describe the problem?

---

<div class="post-metadata">

**Author:** ![peremato](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/peremato/32/29128_2.png) [@peremato](https://discourse.julialang.org/u/peremato)\
**Post date:** [November 19, 2021, 1:58pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/11 "2021-11-19T13:58:36Z")

</div>

> [@jling](#):
>
> These could have been:
> 
> ```julia
> struct Box{T} <: AbstractShape
> x::T; y::T; z::T
> end
> 
> ```

Indeed I am using `struct Box{T} <: AbstractShape`. I just simplified in the reproducer.

---

<div class="post-metadata">

**Author:** ![peremato](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/peremato/32/29128_2.png) [@peremato](https://discourse.julialang.org/u/peremato)\
**Post date:** [November 19, 2021, 2:05pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/12 "2021-11-19T14:05:18Z")

</div>

> [@jling](#):
>
> if you don’t parameterize it, you’re forcing it to be (possibly) slow by forcing abstract type boxing

In the actual code, equivalent to `Figure` is already doubled parametrized: by the floating point type, and by the nested structure because of impossibility to have forward declarations. The actual code looks like this:

```julia
#---Volume-------------------------------------------------------------------------
struct Mother{T,PV}
    label::String
    shape::AbstractShape{T} # Reference to the actual shape
    material::Material # Reference to material
    daughters::Vector{PV} 
end

#---PlacedVolume-------------------------------------------------------------------
struct PlacedVolume{T<:AbstractFloat}
    transformation::Transformation3D{T}
    volume::Mother{T,PlacedVolume{T}}
end
const Volume{T} = Mother{T,PlacedVolume{T}} where T<:AbstractFloat

```

Note that the `Material` in rally should also be abstract since we have different ways to define materials. So, with your suggestion I would have proliferation of new types: ` Volume{T, Shape, Material}`

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [November 19, 2021, 2:15pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/13 "2021-11-19T14:15:16Z")

</div>

I think that is what it is, effectively, if you follow that pattern. You could also parameterize more loosely, like:

```julia
struct Mother{T,S,M,PV}
    label::String
    shape::S # Reference to the actual shape
    material::M # Reference to material
    daughters::Vector{PV} 
end

```

which will allow the types to be concrete, or go for a completely different layout to allow the `Mother` type to be simpler yet concrete. For instance, if instead of the actual `Material` as a field you have a label for the material type (as an integer, for instance, to enumerate the materials).

There are some threads discussing this type of pattern:

> [@Union splitting vs C++](https://discourse.julialang.org/t/union-splitting-vs-c/61772):
>
> This is a continuation from this thread: [Performance drawback with subtyping - #31 by paulmelis](https://discourse.julialang.org/t/performance-drawback-with-subtyping/51939/31) Here we have two codes, which compute someting simple (the sum of the values of a field of a type in an array of mixed types of objects). Typical case where it is hard to be type-stable and avoid run-time dispatch. With 2 different types in the list of types, union splitting kicks in in Julia and the code is fast. With more (in the present example 5 types), it does not. In the C++ code clearly it do…

> [@Performance drawback with subtyping](https://discourse.julialang.org/t/performance-drawback-with-subtyping/51939/9):
>
> Try writing paint2 like this: function paint2(p::Picture) s = 0. for l in p.lines if l isa LineA s += paint(l::LineA) else s += paint(l::LineB) end end s end Does that improve performance?

I am not sure if there is an “ideal” solution (in Julia or other languages). The default performance of what people usually implement in C++ seems to be faster than straightforward Julia implementation of the same thing, but there are a lot of nuances and possibilities which are discussed there.

> [@DNF](#):
>
> This is a syntax error for me. Perhaps you meant

Yes, thanks, I have to rationalize why these things are not equivalent at some point to remember which is the one that works.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [November 19, 2021, 2:20pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/14 "2021-11-19T14:20:44Z")

</div>

> [@lmiq](#):
>
> ```julia
> struct Mother{T,S,M,PV}
> label::String
> shape::S # Reference to the actual shape
> material::M # Reference to material
> daughters::Vector{PV} 
> end
> 
> ```

The `T` parameter is redundant here.

@peremato : One can narrow the types down again, without going overboard with the complexity:

```julia
struct Mother{S<:AbstractShape,M<:Material,PV<:PlacedVolume}
    label::String
    shape::S # Reference to the actual shape
    material::M # Reference to material
    daughters::Vector{PV} 
end

```

The pattern is, you do

```julia
struct MyType{T<:AbstractSuper}
    x::T
end

```

instead of

```julia
struct MyType{T}
    x::AbstractSuper{T}
end

```

You get more concrete fieldtypes without more typing (well, the keyboard kind of typing.)

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [November 19, 2021, 2:22pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/15 "2021-11-19T14:22:22Z")

</div>

Thanks for fixing my poor examples 🙂

---

<div class="post-metadata">

**Author:** ![peremato](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/peremato/32/29128_2.png) [@peremato](https://discourse.julialang.org/u/peremato)\
**Post date:** [November 19, 2021, 2:40pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/16 "2021-11-19T14:40:49Z")

</div>

Thanks for your suggestion but I still have a problem with the second part, which is the nested structures that are coded with `PlacedVolume`.

```julia
struct Mother{T,S,M,PV}
    label::String
    shape::S # Reference to the actual shape
    material::M # Reference to material
    daughters::Vector{PV} 
end

struct PlacedVolume{T,....}
    transformation::Transformation3D{T}
    volume::Mother{T,S,M,PlacedVolume{T,...}}
end

```

I do not know what to put in the `...`. At a given moment I must break the ‘concreteness’ of the geometry. I cannot parametrize a mother volume with all the concrete types of the daughters, and the daughters of the daughters. I can do it for example as I did, at the level of the `Mother` having a member that is abstract (the shape), or I could do it a the `Vector{PV}` by having a `Vector{AbstractPlacement}`, but I would guess I will end with the same performance problems. Your help will be most welcome to find the right pattern.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [November 19, 2021, 2:56pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/17 "2021-11-19T14:56:06Z")

</div>

In terms of expressing it, you can just use the same pattern again:

```julia
struct PlacedVolume{T,M}
    transformation::T
    volume::M
end

```

and then putting constraints inside the braces, if you want. That will clean up the proliferation of parameters.

But this definition becomes mutually recursive. I actually don’t know how well the compiler handles that. You can try?

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [November 19, 2021, 3:04pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/18 "2021-11-19T15:04:14Z")

</div>

> [@peremato](#):
>
> break the ‘concreteness’ of the geometry

you don’t have to enforce the correctness of this with typing system, you could enforce it with the constructor function and simply leave it as what DNF has suggested. Of course, you could always work some of the constraint into the type system if it’s natural and easy to do, but you don’t have to.

---

<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:** [November 19, 2021, 3:10pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/19 "2021-11-19T15:10:43Z")

</div>

If you want polymorphism and performance (both runtime and compile time), I’d suggest trying a package like [Unityper](https://github.com/YingboMa/Unityper.jl) or doing it manually via enums.

---

<div class="post-metadata">

**Author:** ![peremato](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/peremato/32/29128_2.png) [@peremato](https://discourse.julialang.org/u/peremato)\
**Post date:** [November 19, 2021, 3:25pm UTC](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771/20 "2021-11-19T15:25:33Z")

</div>

My brain is exploding because of the recursivelity. The problem is that I have a `Vector` of different shaped ‘daughters’. Either, each volume is the same regardless of the shape (what I did) and then I have an uniform `Vector`, or I do have a different volume for each type of shape and I do have an heterogenous Vector. Clearly I cannot express in a constructor all these complexity. My guess is that having an heterogenous Vector will boil down to the same poor performance.

[Next page](https://discourse.julialang.org/t/dramatic-performance-change-by-adding-additional-unused-method/71771.md?page=2)
