# 10 times more compilation time than expected

**URL:** <https://discourse.julialang.org/t/10-times-more-compilation-time-than-expected/88017>\
**Category:** New to Julia\
**Created:** [September 30, 2022, 1:41am UTC](https://discourse.julialang.org/t/10-times-more-compilation-time-than-expected/88017 "2022-09-30T01:41:55Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Brian1](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brian1/32/49250_2.png) [@Brian1](https://discourse.julialang.org/u/Brian1)\
**Post date:** [September 30, 2022, 1:41am UTC](https://discourse.julialang.org/t/10-times-more-compilation-time-than-expected/88017/1 "2022-09-30T01:41:55Z")

</div>

There is my old version codes:

```julia
struct A
    a::Vector{Float64}
    n::Int
end

test_func(a::A) = begin
    a_vec,a_n = a.a,a.n
    for i = 1 : size(a_vec,1)
        a_vec[i] + a_n
    end
end;

a1 = A(rand(1000_000),1)
a2 = A(rand(1000_000),2)
a3 = A(rand(1000_000),3);
a4 = A(rand(1000_000),4);
a5 = A(rand(1000_000),5);

# run it on a fresh session to find out the compilation time
@time begin
    test_func(a1)
    test_func(a2)
    test_func(a3)
    test_func(a4)
    test_func(a5)
end;

0.002573 seconds

```

The there is my new version codes:

```julia
abstract type A end

struct A1 <: A
    a::Vector{Float64}
end
struct A2 <: A
    a::Vector{Float64}
end
struct A3 <: A
    a::Vector{Float64}
end
struct A4 <: A
    a::Vector{Float64}
end
struct A5 <: A
    a::Vector{Float64}
end

test_func(a::T) where {T<:A1} = begin
    for i = 1 : size(a.a,1)
        a.a[i] + 1
    end
end
test_func(a::T) where {T<:A2} = begin
    for i = 1 : size(a.a,1)
        a.a[i] + 1
    end
end
test_func(a::T) where {T<:A3} = begin
    for i = 1 : size(a.a,1)
        a.a[i] + 1
    end
end
test_func(a::T) where {T<:A4} = begin
    for i = 1 : size(a.a,1)
        a.a[i] + 1
    end
end
test_func(a::T) where {T<:A5} = begin
    for i = 1 : size(a.a,1)
        a.a[i] + 1
    end
end

a1 = A1(rand(1000_000))
a2 = A2(rand(1000_000))
a3 = A3(rand(1000_000));
a4 = A4(rand(1000_000));
a5 = A5(rand(1000_000));

# run it on a fresh session to find out the compilation time
@time begin
    test_func(a1)
    test_func(a2)
    test_func(a3)
    test_func(a4)
    test_func(a5)
end;

 0.020021 seconds (100.72 k allocations: 5.585 MiB, 99.78% compilation time)

```

As can be seen above, the new version code takes more than 10 times time to compile.  
In my thought, the new code is more extentable than the old. So, how can I keep the style of new code and reduce the compilation time to the old?

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [September 30, 2022, 1:52am UTC](https://discourse.julialang.org/t/10-times-more-compilation-time-than-expected/88017/2 "2022-09-30T01:52:20Z")

</div>

The new version has 5 concrete types compared to the old version’s 1 concrete type. In your old version, only `test_func(a1)` required compilation, the other calls just used the existing compiled method `test_func(::A)`. The new version had to compile `test_func(::A1)`, `test_func(::A2)`, `test_func(::A3)`, `test_func(::A4)`, `test_func(::A5)`. I’m not exactly sure why it’s 0.020021/0.002573 = 7.781 times instead of 5, but there are many sources of timing variance that can skew 1-`@time` measurements. A couple examples off the top of my head: heap allocations are nondeterministic, and a multitasking operating system can interrupt the program for quick important background processes.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [September 30, 2022, 2:25am UTC](https://discourse.julialang.org/t/10-times-more-compilation-time-than-expected/88017/3 "2022-09-30T02:25:30Z")

</div>

There are also five constructors that need to be compiled.

Also why do we need `where` statements with these concrete types? Could we just do the following?

```julia
test_func(a::A1) = begin
    for i = 1 : size(a.a,1)
        a.a[i] + 1
    end
end

```

Also I’m not sure this is the best example since the bodies of these functions get compiled away.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [September 30, 2022, 2:30am UTC](https://discourse.julialang.org/t/10-times-more-compilation-time-than-expected/88017/4 "2022-09-30T02:30:34Z")

</div>

I believe the constructors (`A`, `A1`, `A2`, `A3`, `A4`, `A5`) are all compiled upon their calls _prior_ to the timing, though, so their compilation isn’t being counted.

---

<div class="post-metadata">

**Author:** ![Brian1](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brian1/32/49250_2.png) [@Brian1](https://discourse.julialang.org/u/Brian1)\
**Post date:** [September 30, 2022, 4:26am UTC](https://discourse.julialang.org/t/10-times-more-compilation-time-than-expected/88017/5 "2022-09-30T04:26:52Z")

</div>

The difference between 7.1 and 5 might just be caused by random pulse from system.  
That is to say:

```julia
abstract type A end
struct A1 <: A
    a::Vector{Float64}
end
struct A2 <: A
    a::Vector{Float64}
end
.
.
.

```

Is there a way I can inform the compiler that `test_func` is all the same for all `A`, so that after compiling for `test_func(x::A1)`, then `test_func(x::A2)` can use the compile result of `test_func(x::A1)` ?

---

<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:** [September 30, 2022, 5:31am UTC](https://discourse.julialang.org/t/10-times-more-compilation-time-than-expected/88017/6 "2022-09-30T05:31:01Z")

</div>

> [@Brian1](#):
>
> Is there a way I can inform the compiler that `test_func` is all the same for all `A`, so that after compiling for `test_func(x::A1)`, then `test_func(x::A2)` can use the compile result of `test_func(x::A1)` ?

no because they are different types and different functions, don’t make them different concrete types if you don’t want them to be different, use `NamedTuple` or whatever, which is “anonymous” in the sense that their type is defined by field names and field types

* * *

what are you trying to achieve here? so far the code snippet is equivalent to if you never defined `A2 - A5` as separate types and instead just do

```julia
const A2 = const A3 = const A4 = const A5 = A1

```

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [September 30, 2022, 6:48am UTC](https://discourse.julialang.org/t/10-times-more-compilation-time-than-expected/88017/7 "2022-09-30T06:48:16Z")

</div>

Well, if OP really wants to dispatch to different methods with different baked-in constants, OP must compile each of those methods and use different concrete input types; making `A2`-`A5` aliases for `A1` will cause those definitions to override each other so `test_func` only has 1 method. For such simple constants, I’d rather refactor those types as members of a parametric struct `struct A{N} a::Vector{Float64} end` so I can just write the one parametric method `function test_funct(a::A{N}) where N`, but that method will still be compiled separately for each concrete type e.g. `A{1}`, `A{2}`.

There is just a tradeoff between putting work in runtime vs compile-time (dispatching on more types and compiling more methods). In OP’s example, there doesn’t seem to be much benefit to moving `n` from runtime to compile-time, but there might be more complicated cases where the approach is worth it.

---

<div class="post-metadata">

**Author:** ![Brian1](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brian1/32/49250_2.png) [@Brian1](https://discourse.julialang.org/u/Brian1)\
**Post date:** [October 4, 2022, 9:56am UTC](https://discourse.julialang.org/t/10-times-more-compilation-time-than-expected/88017/8 "2022-10-04T09:56:06Z")

</div>

I find another way to do it:

Old codes:

```julia
abstract type A end

struct A1 <: A
    a::Vector{Float64}
end

struct A2 <: A
    a::Vector{Float64}
end

struct A3 <: A
    a::Vector{Float64}
end

struct A4 <: A
    a::Vector{Float64}
end

struct A5 <: A
    a::Vector{Float64}
end

struct A6 <: A
    a::Vector{Float64}
end

test_func(x::T) where {T<:A} = begin
    res = similar(x.a)
    for i = 1 : size(x.a,1)
        res[i] = x.a[i] + 1
    end
    return res
end

a1 = A1(randn(100_000))
a2 = A2(randn(100_000))
a3 = A3(randn(100_000))
a4 = A4(randn(100_000))
a5 = A5(randn(100_000))
a6 = A6(randn(100_000));

@timev begin
    test_func(a1)
    test_func(a2)
    test_func(a3)
    test_func(a4)
    test_func(a5)
    test_func(a6)
end;
 0.059963 seconds (127.07 k allocations: 11.560 MiB, 96.75% compilation time)
elapsed time (ns): 59962600
gc time (ns): 0
bytes allocated: 12121391
pool allocs: 127066
non-pool GC allocs: 0
malloc() calls: 6
free() calls: 0
minor collections: 0
full collections: 0

```

New codes:

```julia
abstract type A end

struct A1 <: A
    a::Vector{Float64}
end

struct A2 <: A
    a::Vector{Float64}
end

struct A3 <: A
    a::Vector{Float64}
end

struct A4 <: A
    a::Vector{Float64}
end

struct A5 <: A
    a::Vector{Float64}
end

struct A6 <: A
    a::Vector{Float64}
end

struct Wrapper
    a::Vector{Float64}
    n::Int
end

Wrapper(x::A) = Wrapper(x.a,1)

test_func(x::T) where {T<:Wrapper} = begin
    res = similar(x.a)
    for i = 1 : size(x.a,1)
        res[i] = x.a[i] + x.n
    end
    return res
end

a1 = A1(randn(100_000))
a2 = A2(randn(100_000))
a3 = A3(randn(100_000))
a4 = A4(randn(100_000))
a5 = A5(randn(100_000))
a6 = A6(randn(100_000));

@timev begin
    a1 = Wrapper(a1)
    a2 = Wrapper(a2)
    a3 = Wrapper(a3)
    a4 = Wrapper(a4)
    a5 = Wrapper(a5)
    a6 = Wrapper(a6)

    test_func(a1)
    test_func(a2)
    test_func(a3)
    test_func(a4)
    test_func(a5)
    test_func(a6)
end;
  0.021478 seconds (31.44 k allocations: 6.388 MiB, 93.96% compilation time)
elapsed time (ns): 21478000
gc time (ns): 0
bytes allocated: 6698444
pool allocs: 31438
non-pool GC allocs: 0
malloc() calls: 6
free() calls: 0
minor collections: 0
full collections: 0

```

As can be seen above, the new code is 3x faster than the old.  
The old codes compile `test_func` on `A1,A2,A3,A4,A5,A6`, 6 times. The new codes complie `Wrapper` on `A1,A2,A3,A4,A5,A6` and `test_func` on `Wrapper`, all sum to 7 times. But the time of compiling on `Wrapper` is more easier than `test_func`. In my real situation, `test_func` is a bit more complicated, so the gap in speed between old codes and new codes can be up to 12x.

In the aspect of extention, if I have a subtype of `A`: `A7`, which should be designed to plus 2.  
In the old codes , I would define:

```julia
struct A7 <: A
    a::Vector{Float64}
end

test_func(x::T) where {T<:A7} = begin
    res = similar(x.a)
    for i = 1 : size(x.a,1)
        res[i] = x.a[i] + 2
    end
    return res
end

```

In the new codes:

```julia
struct A7 <: A
    a::Vector{Float64}
end

Wrapper(x::A7) = Wrapper(x.a,2)

```

In my guess, If the compilation can compile in `abstract type`, the compilation time on the first running would be reduced much?

---

<div class="post-metadata">

**Author:** ![Brian1](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brian1/32/49250_2.png) [@Brian1](https://discourse.julialang.org/u/Brian1)\
**Post date:** [October 4, 2022, 10:00am UTC](https://discourse.julialang.org/t/10-times-more-compilation-time-than-expected/88017/9 "2022-10-04T10:00:43Z")

</div>

I am trying to keep the ability of extension and reduce the first running time.

> [@Brian1](#):
>
> In the aspect of extention

---

<div class="post-metadata">

**Author:** ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)\
**Post date:** [October 4, 2022, 1:14pm UTC](https://discourse.julialang.org/t/10-times-more-compilation-time-than-expected/88017/10 "2022-10-04T13:14:25Z")

</div>

Very hard to say given the highly limited context provided here (can you explain what you’re actually trying to do?), but it sounds a lot like the `Wrapper` approach is better anyway since adding some offset value is probably not a good use of the type system.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [October 4, 2022, 5:38pm UTC](https://discourse.julialang.org/t/10-times-more-compilation-time-than-expected/88017/11 "2022-10-04T17:38:58Z")

</div>

Um, I just noticed that in both those versions, you’re just adding 1 to `x.a[i]`. The Wrapper version adds `x.n`, sure, but you only defined `Wrapper(x::A) = Wrapper(x.a,1)` so `x.n` is 1 in all the compiled specializations for all subtypes of `A`. In your original `struct A` code you store `a_n` with different integers, not just 1.
