# Loop unrolling for type stability

**URL:** <https://discourse.julialang.org/t/loop-unrolling-for-type-stability/91565>\
**Category:** New to Julia\
**Tags:** question\
**Created:** [December 12, 2022, 6:14pm UTC](https://discourse.julialang.org/t/loop-unrolling-for-type-stability/91565 "2022-12-12T18:14:19Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![smartalecH](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/smartalech/32/32379_2.png) [@smartalecH](https://discourse.julialang.org/u/smartalecH)\
**Post date:** [December 12, 2022, 6:14pm UTC](https://discourse.julialang.org/t/loop-unrolling-for-type-stability/91565/1 "2022-12-12T18:14:19Z")

</div>

I (naively) thought that the compiler would do some basic loop unrolling. This is particularly important for generating type-stable code. Is there a way I can easily make the following code snippet stable?

```julia
abstract type A end
struct Ax <: A end
struct Ay <: A end
struct Az <: A end

bar(a::Ax) = println("Ax")
bar(a::Ay) = println("Ay")
bar(a::Az) = println("Az")

function foo()
    for a in (Ax(), Ay(), Az())
        bar(a)
    end
end

foo()

```

The `@code_warnings` output is

```julia
julia> @code_warntype foo()
MethodInstance for foo()
  from foo() in Main at <filetree>
Arguments
  #self#::Core.Const(foo)
Locals
  @_2::Union{Nothing, Tuple{Union{Ax, Ay, Az}, Int64}}
  a::Union{Ax, Ay, Az}
Body::Nothing
1 ─ %1 = Main.Ax()::Core.Const(Ax())
│ %2 = Main.Ay()::Core.Const(Ay())
│ %3 = Main.Az()::Core.Const(Az())
│ %4 = Core.tuple(%1, %2, %3)::Core.Const((Ax(), Ay(), Az()))
│ (@_2 = Base.iterate(%4))
│ %6 = (@_2::Core.Const((Ax(), 2)) === nothing)::Core.Const(false)
│ %7 = Base.not_int(%6)::Core.Const(true)
└── goto #4 if not %7
2 ┄ %9 = @_2::Tuple{Union{Ax, Ay, Az}, Int64}
│ (a = Core.getfield(%9, 1))
│ %11 = Core.getfield(%9, 2)::Int64
│ Main.bar(a)
│ (@_2 = Base.iterate(%4, %11))
│ %14 = (@_2 === nothing)::Bool
│ %15 = Base.not_int(%14)::Bool
└── goto #4 if not %15
3 ─ goto #2
4 ┄ return nothing

```

where `a::Union{Ax, Ay, Az}` and `Tuple{Union{Ax, Ay, Az}, Int64}` are red (not type stable).

Obviously, I could _manually_ unroll the loop…

```julia
function foo_manual()
    bar(Ax())
    bar(Ay())
    bar(Az())
end

```

…which is indeed type stable. But this doesn’t scale for larger problems (plus this doesn’t seem very “Julian”).

Thanks in advance!

---

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [December 12, 2022, 6:17pm UTC](https://discourse.julialang.org/t/loop-unrolling-for-type-stability/91565/2 "2022-12-12T18:17:14Z")

</div>

This package does the “manual” unrolling for you:

> **[GitHub - cstjean/Unrolled.jl: Unrolling loops at compile-time](https://github.com/cstjean/Unrolled.jl)**
>
> Unrolling loops at compile-time. Contribute to cstjean/Unrolled.jl development by creating an account on GitHub.

---

<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:** [December 12, 2022, 6:18pm UTC](https://discourse.julialang.org/t/loop-unrolling-for-type-stability/91565/3 "2022-12-12T18:18:29Z")

</div>

`map` with Tuple arguments tends to be the easiest way.

---

<div class="post-metadata">

**Author:** ![smartalecH](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/smartalech/32/32379_2.png) [@smartalecH](https://discourse.julialang.org/u/smartalecH)\
**Post date:** [December 12, 2022, 6:20pm UTC](https://discourse.julialang.org/t/loop-unrolling-for-type-stability/91565/4 "2022-12-12T18:20:26Z")

</div>

> This package does the “manual” unrolling for you:

Thanks! I thought the same, but it doesn’t appear to work with this example:

```julia
using Unrolled

abstract type A end
struct Ax <: A end
struct Ay <: A end
struct Az <: A end

bar(a::Ax) = println("Ax")
bar(a::Ay) = println("Ay")
bar(a::Az) = println("Az")

@unroll function foo()
    for a in (Ax(), Ay(), Az())
        bar(a)
    end
end

```

```julia
@code_warntype foo()
MethodInstance for foo()
  from foo() in Main at <>
Arguments
  #self#::Core.Const(foo)
Locals
  @_2::Union{Nothing, Tuple{Union{Ax, Ay, Az}, Int64}}
  a::Union{Ax, Ay, Az}
Body::Nothing
1 ─ %1 = Main.Ax()::Core.Const(Ax())
│ %2 = Main.Ay()::Core.Const(Ay())
│ %3 = Main.Az()::Core.Const(Az())
│ %4 = Core.tuple(%1, %2, %3)::Core.Const((Ax(), Ay(), Az()))
│ (@_2 = Base.iterate(%4))
│ %6 = (@_2::Core.Const((Ax(), 2)) === nothing)::Core.Const(false)
│ %7 = Base.not_int(%6)::Core.Const(true)
└── goto #4 if not %7
2 ┄ %9 = @_2::Tuple{Union{Ax, Ay, Az}, Int64}
│ (a = Core.getfield(%9, 1))
│ %11 = Core.getfield(%9, 2)::Int64
│ Main.bar(a)
│ (@_2 = Base.iterate(%4, %11))
│ %14 = (@_2 === nothing)::Bool
│ %15 = Base.not_int(%14)::Bool
└── goto #4 if not %15
3 ─ goto #2
4 ┄ return nothing

```

same red as before.

---

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [December 12, 2022, 6:24pm UTC](https://discourse.julialang.org/t/loop-unrolling-for-type-stability/91565/5 "2022-12-12T18:24:52Z")

</div>

This works:

```julia
julia> @unroll function foo(seq)
           @unroll for a in seq
               bar(a)
           end
       end
foo_unrolled_expansion_##314 (generic function with 1 method)

julia> @code_warntype foo((Ax(), Ay(), Az()))
MethodInstance for foo(::Tuple{Ax, Ay, Az})
  from foo(seq) in Main at /home/jishnu/.julia/packages/Unrolled/nMVH3/src/Unrolled.jl:127
Arguments
  #self#::Core.Const(foo)
  seq::Core.Const((Ax(), Ay(), Az()))
Locals
  a@_3::Ax
  a@_4::Ay
  a@_5::Az
Body::Nothing
1 ─ %1 = Base.getindex(seq, 1)::Core.Const(Ax())
│ (a@_3 = %1)
│ Main.bar(a@_3)
│ %4 = Base.getindex(seq, 2)::Core.Const(Ay())
│ (a@_4 = %4)
│ Main.bar(a@_4)
│ %7 = Base.getindex(seq, 3)::Core.Const(Az())
│ (a@_5 = %7)
│ Main.bar(a@_5)
│ %10 = Main.nothing::Core.Const(nothing)
└── return %10

```

---

<div class="post-metadata">

**Author:** ![smartalecH](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/smartalech/32/32379_2.png) [@smartalecH](https://discourse.julialang.org/u/smartalecH)\
**Post date:** [December 12, 2022, 6:26pm UTC](https://discourse.julialang.org/t/loop-unrolling-for-type-stability/91565/6 "2022-12-12T18:26:04Z")

</div>

> `map` with Tuple arguments tends to be the easiest way.

Aha! This works!

```julia
struct Ax <: A end
struct Ay <: A end
struct Az <: A end

bar(a::Ax) = println("Ax")
bar(a::Ay) = println("Ay")
bar(a::Az) = println("Az")

function foo()
    map(bar,(Ax(), Ay(), Az()))
    return
end

foo()

```

```julia
julia> @code_warntype foo()
MethodInstance for foo()
  from foo() in <>
Arguments
  #self#::Core.Const(foo)
Body::Nothing
1 ─ %1 = Main.Ax()::Core.Const(Ax())
│ %2 = Main.Ay()::Core.Const(Ay())
│ %3 = Main.Az()::Core.Const(Az())
│ %4 = Core.tuple(%1, %2, %3)::Core.Const((Ax(), Ay(), Az()))
│ Main.map(Main.bar, %4)
└── return nothing

```

---

<div class="post-metadata">

**Author:** ![smartalecH](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/smartalech/32/32379_2.png) [@smartalecH](https://discourse.julialang.org/u/smartalecH)\
**Post date:** [December 12, 2022, 6:27pm UTC](https://discourse.julialang.org/t/loop-unrolling-for-type-stability/91565/7 "2022-12-12T18:27:53Z")

</div>

> This works:

Ah I see. The trick is to pass the sequence as a function argument. I didn’t catch that nuance at first.

Thanks!
