# Why is functional style so much slower here?

**URL:** https://discourse.julialang.org/t/why-is-functional-style-so-much-slower-here/107657
**Category:** New to Julia
**Created:** [December 15, 2023, 11:40am UTC](https://discourse.julialang.org/t/why-is-functional-style-so-much-slower-here/107657 "2023-12-15T11:40:16Z")
**Posts on this page:** 9
**Page:** 1

<div class="post-metadata">

### Author: ![glebm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/glebm/32/205499_2.png) [@glebm](https://discourse.julialang.org/u/glebm)
#### Post date: [December 15, 2023, 11:40am UTC](https://discourse.julialang.org/t/why-is-functional-style-so-much-slower-here/107657/1 "2023-12-15T11:40:16Z")

</div>

I wrote my first program in Julia today, which is a solution to Advent of Code day 15.

I wrote it in a functional style at first and was very surprised with how slow it ran.

I narrowed it down to the `score` functions. Here are 3 versions of it:

```julia
# Using .|> and |>
score_pipe((boxnum, box)::Tuple{Int, Vector{Lens}})::Int =
  box |> enumerate .|>
  (((slot, lens)::Tuple{Int, Lens},) -> boxnum * slot * lens.focal)
  |> sum

# Using sum(f, iter)
score_sum_fn((boxnum, box)::Tuple{Int, Vector{Lens}})::Int =
  sum(enumerate(box); init=0) do (slot, lens)::Tuple{Int, Lens}
    boxnum * slot * lens.focal
  end

# Using a for loop
function score_loop((boxnum, box)::Tuple{Int, Vector{Lens}})::Int
  result = 0
  for (slot, lens) in enumerate(box)
    result += boxnum * slot * lens.focal
  end
  result
end

```

```julia
@time sum(Iterators.map(score, enumerate(boxes))) |> println

```

```julia
1. score_pipe: 0.108080 seconds (237.70 k allocations: 16.374 MiB, 99.81% compilation time)
2. score_sum_fn: 0.082646 seconds (178.00 k allocations: 12.024 MiB, 99.75% compilation time)
3. score_loop: 0.068665 seconds (112.36 k allocations: 7.630 MiB, 99.73% compilation time)

```

1. Why is there such a huge difference between 3 and 1? Is it bad practice to write functional code in Julia? That would be unfortunate.
2. Why do any of these do allocations at all? They’re simply iterating over a vector and accumulating everything into a single number, there shouldn’t be any heap allocations.

Full code: [https://replit.com/@glebm1/Julia-AoC-2023-Day-15#main.jl](https://replit.com/@glebm1/Julia-AoC-2023-Day-15#main.jl)

---

<div class="post-metadata">

### Author: ![Eben60](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/eben60/32/13475_2.png) [@Eben60](https://discourse.julialang.org/u/Eben60)
#### Post date: [December 15, 2023, 12:09pm UTC](https://discourse.julialang.org/t/why-is-functional-style-so-much-slower-here/107657/2 "2023-12-15T12:09:03Z")

</div>

For the first question: Most of the time you measure is compilation time anyway.

For benchmarking, use [BenchmarkTools.jl](https://juliaci.github.io/BenchmarkTools.jl/stable/#BenchmarkTools)

---

<div class="post-metadata">

### Author: ![glebm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/glebm/32/205499_2.png) [@glebm](https://discourse.julialang.org/u/glebm)
#### Post date: [December 15, 2023, 12:14pm UTC](https://discourse.julialang.org/t/why-is-functional-style-so-much-slower-here/107657/3 "2023-12-15T12:14:00Z")

</div>

Oh, do `allocations` also include allocations for compilation?

---

<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: [December 15, 2023, 12:19pm UTC](https://discourse.julialang.org/t/why-is-functional-style-so-much-slower-here/107657/4 "2023-12-15T12:19:34Z")

</div>

> [@glebm](#):
>
> ```julia
> 1. score_pipe: 0.108080 seconds (237.70 k allocations: 16.374 MiB, 99.81% compilation time)
> 2. score_sum_fn: 0.082646 seconds (178.00 k allocations: 12.024 MiB, 99.75% compilation time)
> 3. score_loop: 0.068665 seconds (112.36 k allocations: 7.630 MiB, 99.73% compilation time)
> 
> ```

Note how more than 99% of your timing is compilation time. All of your codes are pretty much equal when accounted for that:

```julia
julia> 0.108080 * (1.0 - .9981)
0.00020535200000000136

julia> 0.082646 * (1.0 - .9975)
0.00020661499999999558

julia> 0.068665 * (1.0 - .9973)
0.00018539550000000246

```

> [@glebm](#):
>
> Oh, do `allocations` also include allocations for compilation?

`@time` is a very naive timing mechanism. It includes _everything_, no matter whether its compilation or GC work that just happened to occur during its invocation. It also only measures once, so is subject to noise on tight measurements.

---

<div class="post-metadata">

### Author: ![barucden](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/barucden/32/26154_2.png) [@barucden](https://discourse.julialang.org/u/barucden)
#### Post date: [December 15, 2023, 12:37pm UTC](https://discourse.julialang.org/t/why-is-functional-style-so-much-slower-here/107657/5 "2023-12-15T12:37:30Z")

</div>

Also, someone correct me if I am wrong, but the type annotations in your code are completely unnecessary here. They can be useful to restrict valid input types of the function arguments but the other ones are just distraction.

---

<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: [December 17, 2023, 2:55am UTC](https://discourse.julialang.org/t/why-is-functional-style-so-much-slower-here/107657/6 "2023-12-17T02:55:22Z")

</div>

Technically they are acting as type assertions for the return type as well. That’s one way to ensure type stability.

---

<div class="post-metadata">

### Author: ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)
#### Post date: [December 17, 2023, 7:02am UTC](https://discourse.julialang.org/t/why-is-functional-style-so-much-slower-here/107657/7 "2023-12-17T07:02:56Z")

</div>

Running `@time` twice gets rid of the compilation time and is often good enough.

---

<div class="post-metadata">

### Author: ![lrnv](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lrnv/32/19373_2.png) [@lrnv](https://discourse.julialang.org/u/lrnv)
#### Post date: [December 22, 2023, 5:57pm UTC](https://discourse.julialang.org/t/why-is-functional-style-so-much-slower-here/107657/8 "2023-12-22T17:57:12Z")

</div>

Well no it does not ensure type stability, but only “hide” type instability behind a barrier. They are still there.

---

<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 22, 2023, 6:19pm UTC](https://discourse.julialang.org/t/why-is-functional-style-so-much-slower-here/107657/9 "2023-12-22T18:19:45Z")

</div>

> **[Type Stability in Julia: Avoiding Performance Pathologies in JIT Compilation...](https://arxiv.org/abs/2109.01950)**
>
> As a scientific programming language, Julia strives for performance but also provides high-level productivity features. To avoid performance pathologies, Julia users are expected to adhere to a coding discipline that enables so-called type stability....

Defined distinct notions of type stability and type groundedness.

Type stability (return type inserted) is enough to prevent propagation. Propagating instabilities can of course be quite bad.

The functions that take up most of your runtime being grounded is enough for instabilities to not be a major runtime performance concern (but they can still negatively impact compile times!). E.g., a function barrier is grounded, but the code calling it isn’t.
