# Too many precompilations

**URL:** <https://discourse.julialang.org/t/too-many-precompilations/48416>\
**Category:** General Usage\
**Tags:** question\
**Created:** [October 15, 2020, 8:48am UTC](https://discourse.julialang.org/t/too-many-precompilations/48416 "2020-10-15T08:48:44Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![kogad](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kogad/32/18629_2.png) [@kogad](https://discourse.julialang.org/u/kogad)\
**Post date:** [October 15, 2020, 8:48am UTC](https://discourse.julialang.org/t/too-many-precompilations/48416/1 "2020-10-15T08:48:44Z")

</div>

Hi all,

I am implementing genetic proggraming (GP) in julia. In GP, I need to evaluate mathematical expressions represented by tree structures.  
This is extracted and simplified code relating to the evaluation.

```julia
struct FuncNode{S, T, U}
    func::S
    args::Tuple{T, U}
end

struct ConstNode{T <: Real}
    value::T
end

function evaluate(node::FuncNode)
    arg1 = evaluate(node.args[1])
    arg2 = evaluate(node.args[2])
    return node.func(arg1, arg2)
end

add_trees(tree1, tree2) = FuncNode(+, (tree1, tree2))
    
evaluate(node::ConstNode) = node.value

tree1 = FuncNode(+,
            (FuncNode(+, 
                (ConstNode(1), 
                 ConstNode(2))),
            FuncNode(-,
                (ConstNode(3),
                 ConstNode(4)))))
tree2 = FuncNode(/, (ConstNode(1), ConstNode(1)))

@show evaluate(tree1)
@show evaluate(add_trees(tree1, tree2))

```

In an original code, there is a function to create trees and thousands of trees are generated randomly and then evaluated. When I run the original code with `--trace-compile=stderr `, it seems many precompilation occur when creating and evaluating trees like this:

```julia
( I picked up some messages, because there are too many messages......)
precompile(Tuple{Type{JuliaGSGP.FuncNode{T, S, U} where U where S where T}, JuliaGSGP.PrimitiveFunction{typeof(Base.:(+))}, Tuple{JuliaGSGP.FuncNode{JuliaGSGP.FuncNode{JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(*))}, JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(+))}, typeof(JuliaGSGP.protected_div)}, JuliaGSGP.FuncNode{JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(-))}, JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(JuliaGSGP.protected_div)}, typeof(Base.:(+))}, typeof(Base.:(-))}, JuliaGSGP.FuncNode{JuliaGSGP.FuncNode{JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(JuliaGSGP.protected_div)}, JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(-))}, typeof(JuliaGSGP.protected_div)}, JuliaGSGP.FuncNode{JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(JuliaGSGP.protected_div)}, JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(*))}, typeof(Base.:(+))}, typeof(JuliaGSGP.protected_div)}}})
precompile(Tuple{Type{JuliaGSGP.FuncNode{T, S, U} where U where S where T}, JuliaGSGP.PrimitiveFunction{typeof(Base.:(*))}, Tuple{JuliaGSGP.FuncNode{JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(-))}, JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(*))}, typeof(Base.:(+))}, JuliaGSGP.FuncNode{JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(+))}, JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(-))}, typeof(JuliaGSGP.protected_div)}}})
precompile(Tuple{Type{JuliaGSGP.FuncNode{T, S, U} where U where S where T}, JuliaGSGP.PrimitiveFunction{typeof(Base.:(+))}, Tuple{JuliaGSGP.FuncNode{JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(*))}, JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(-))}, typeof(Base.:(-))}, JuliaGSGP.FuncNode{JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(-))}, JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(+))}, typeof(Base.:(+))}}})
precompile(Tuple{Type{JuliaGSGP.FuncNode{T, S, U} where U where S where T}, JuliaGSGP.PrimitiveFunction{typeof(JuliaGSGP.protected_div)}, Tuple{JuliaGSGP.FuncNode{JuliaGSGP.FuncNode{JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(-))}, JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(*))}, typeof(Base.:(+))}, JuliaGSGP.FuncNode{JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(+))}, JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(-))}, typeof(JuliaGSGP.protected_div)}, typeof(Base.:(*))}, JuliaGSGP.FuncNode{JuliaGSGP.FuncNode{JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(*))}, JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(-))}, typeof(Base.:(-))}, JuliaGSGP.FuncNode{JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(-))}, JuliaGSGP.FuncNode{JuliaGSGP.Variable{Int64}, JuliaGSGP.Variable{Int64}, typeof(Base.:(+))}, typeof(Base.:(+))}, typeof(Base.:(+))}}})

```

This compilations slowing down the performance. I think this is because `FuncNode` is parameterized by various tree structures, but I’m not sure.  
What approach can I take to deal with this problem?

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [October 15, 2020, 10:47am UTC](https://discourse.julialang.org/t/too-many-precompilations/48416/2 "2020-10-15T10:47:24Z")

</div>

Are you trying to learn the language? Because I am positive that there will be some package for genetic algorithms out there, like this one: [GitHub - wildart/Evolutionary.jl: Evolutionary & genetic algorithms for Julia](https://github.com/wildart/Evolutionary.jl)

Also JuliaHub is your friend: [JuliaHub](https://juliahub.com/ui/Packages?q=genetic)

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [October 15, 2020, 11:07am UTC](https://discourse.julialang.org/t/too-many-precompilations/48416/3 "2020-10-15T11:07:00Z")

</div>

> [@kogad](#):
>
> This compilations slowing down the performance. I think this is because `FuncNode` is parameterized by various tree structures, but I’m not sure.  
> What approach can I take to deal with this problem?

Yes, since the type of a tuple is built from the type of their elements. If you want to keep it more generic, use `Vector` here instead. If that’s too slow and you know you’ll only deal with operators with a small number of arguments, try StaticArrays.jl.

---

<div class="post-metadata">

**Author:** ![kogad](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kogad/32/18629_2.png) [@kogad](https://discourse.julialang.org/u/kogad)\
**Post date:** [October 15, 2020, 11:47am UTC](https://discourse.julialang.org/t/too-many-precompilations/48416/4 "2020-10-15T11:47:16Z")

</div>

I know there are some packages for GA and I used them for reference, but I didn’t know JuliaHub. It looks useful! Thanks!

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [October 15, 2020, 11:56am UTC](https://discourse.julialang.org/t/too-many-precompilations/48416/5 "2020-10-15T11:56:59Z")

</div>

> [@Sukera](#):
>
> Yes, since the type of a tuple is built from the type of their elements. If you want to keep it more generic, use `Vector` here instead. If that’s too slow and you know you’ll only deal with operators with a small number of arguments, try StaticArrays.jl.

I don’t think using `StaticArrays` will work,  
under the hood a static array is a tuple built up of the type of its elements.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [October 15, 2020, 12:04pm UTC](https://discourse.julialang.org/t/too-many-precompilations/48416/6 "2020-10-15T12:04:08Z")

</div>

> [@oxinabox](#):
>
> under the hood a static array is a tuple built up of the type of its elements.

Yes, but I believe `SVector{Union{FuncNode,ConstNode},2}` is a concrete type and easier to infer than a long chain of nested `FuncNode` types. Also only leads to two specializations of `evaluate` - on `FuncNode` and `ConstNode`. That of course leads to a degraded inferred total return type.

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [October 15, 2020, 12:24pm UTC](https://discourse.julialang.org/t/too-many-precompilations/48416/7 "2020-10-15T12:24:23Z")

</div>

> [@kogad](#):
>
> ```julia
> struct FuncNode{S, T, U}
> func::S
> args::Tuple{T, U}
> end
> 
> struct ConstNode{T <: Real}
> value::T
> end
> 
> ```

I think what you should do is just drop the type-parameters.

```julia
struct FuncNode
    func
    args::Tuple
end

struct ConstNode
    value::Real
end

```

The reason to make them concretely typed via type-parameters is so that the compiler can generate specialized code for that data structure,  
so it can do things like knowing that `node.func(evalate(node.args[1]), evaluate(node.args[2]))` is actually `+(Int, Int)`, which can actually be inlined to the LLVM call to the intrinstical for integer addition.

It is to let the compiler compile specialized methods, so that it can make it fast for if you are going to call it a lot of times.  
At the cost of **much** more expensive first call time, as it has to first run compilation, then actually run it.

If it is type parameteric then basically every call to `evaluate(node::FuncNode)` is going to need to compile new code, since almost every combination of `{S, T, U}` will be unique.  
In contrast, if you don’t have it then only one method specializationm of `evaluate(node::FuncNode)` will need to be compiled.

Now, revisiting what i said before.  
The point of all this compilation is to generate faster code for particular inputs, so that calling them again and again will be fast.  
At the cost of much higher cost for first time to run, because of the compilation.

Bui you are doing genetic programming.  
Basically every `FuncNode` repesents program that will only be run **exactly once** for evaluation.  
it doesn’t get run again and again.  
So the specialization basically only hurts.  
The things that specialization can do in this case are not super powerful anyway, in this case, i don’t think.

Potentially you mighjt want to particually specialize, just on the `func` and ion the `ConstNode`,  
since you won’t see too many different ones of those.

```julia
struct FuncNode{S}
    func::S
    args::Tuple
end

struct ConstNode{T<:Real}
    value::T
end

```

You could even go a bit further have have `FuncNode` be an abstract type,  
with different concreate types depending on if at a leaf or in a branch in the expression tree.  
Since it might well be that you have a lot of outer leafs that occur in many programs that might be worth specalizing

```julia
abstract type FuncNode end
struct LeafFuncNode{S, A <: ConstNode, B<:ConstNode} <: FuncNode
    func::S
    args::Tuple{A, B}
end

struct BranchFuncNode{S} <: FuncNode
    func::S
    args::Tuple
end

FuncNode(func, (a, b)) = BranchFuncNode(func, (a, b))
FuncNode(func, (a, b)::Tuple{ConstNode, ConstNode}) = LeafFuncNode(func, (a, b))

struct ConstNode{T<:Real}
    value::T
end

```

You can keep going down this pass with e.g. parents of 2 `LeafFunNodes` being specialized.  
Which lets you trade off between speed of common subparts, vs compile time.  
But i think it won’t do much because there is not all that much the compiler can do in specializeing `evaluate` to make if faster

* * *

I have heard it said that the process of learning julia type parameters/field typing is:

1. First you think it doesn’t matter, and you have a bunch of abstractly typed fields
2. Then you learn it matters, and you add a lot of type parameters, to avoid abstractly typed fields
3. Then you realize that it actually doesn’t matter a lot of the time, and you have some abstractly typed fields and some non-abstractly typed fields.

---

<div class="post-metadata">

**Author:** ![johnh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/johnh/32/3615_2.png) [@johnh](https://discourse.julialang.org/u/johnh)\
**Post date:** [October 15, 2020, 1:41pm UTC](https://discourse.julialang.org/t/too-many-precompilations/48416/8 "2020-10-15T13:41:58Z")

</div>

@oxinabox It still feels conterintuitive to me - you would think that by specifying types you are HELPING the compiler. I guess you are, but probably not in the way that you think you are.

---

<div class="post-metadata">

**Author:** ![kogad](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kogad/32/18629_2.png) [@kogad](https://discourse.julialang.org/u/kogad)\
**Post date:** [October 16, 2020, 2:02am UTC](https://discourse.julialang.org/t/too-many-precompilations/48416/9 "2020-10-16T02:02:53Z")

</div>

That’s very helpful. Just dropping type parameters, the code became several times faster!  
Thanks a lot!
