# Seemingly unnecessary code cruft in a compiled higher-order function

**URL:** <https://discourse.julialang.org/t/seemingly-unnecessary-code-cruft-in-a-compiled-higher-order-function/26357>\
**Category:** Performance\
**Created:** [July 14, 2019, 6:14pm UTC](https://discourse.julialang.org/t/seemingly-unnecessary-code-cruft-in-a-compiled-higher-order-function/26357 "2019-07-14T18:14:28Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![xor0110](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xor0110/32/7926_2.png) [@xor0110](https://discourse.julialang.org/u/xor0110)\
**Post date:** [July 14, 2019, 6:14pm UTC](https://discourse.julialang.org/t/seemingly-unnecessary-code-cruft-in-a-compiled-higher-order-function/26357/1 "2019-07-14T18:14:28Z")

</div>

Read this post if you care about zero-cost abstractions.

This is a test of Julia’s ability to produce highly optimized code for higher-order functions, taking a function as an argument. The two functions are equivalent, and the compiled code is mostly identical, except for the first function there is a penalty of 2ns due to some “cruft” at the generated machine code before and after the core of the function. This seems to have something to do with GC. Any idea why this happens? Can it be prevented by the user, or maybe optimized away by the compiler?

```julia
using BenchmarkTools

ilovehofs(a::Vector{T}) where T = myfoldl(+, 0f0, A) # EDIT: this was just a silly typo

function myfoldl(r, init::T, aa::Vector{T}) where T
    @inbounds begin
        acc = init
        @simd for a in aa
            acc = r(acc, a)
        end
    end
    acc
end

function ihatehofs(A::Vector{T}) where T
    @inbounds begin
        a = zero(eltype(A))
        @simd for ai in A
            a = a + ai
        end
    end
    a
end

A = rand(Float32, 1000)

@code_llvm ilovehofs(A)
@code_llvm ihatehofs(A)

@btime ilovehofs($A)
@btime ihatehofs($A)

```

---

<div class="post-metadata">

**Author:** ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)\
**Post date:** [July 14, 2019, 6:47pm UTC](https://discourse.julialang.org/t/seemingly-unnecessary-code-cruft-in-a-compiled-higher-order-function/26357/2 "2019-07-14T18:47:12Z")

</div>

They seem to perform the same?

```julia
julia> @btime ilovehofs(a)
  106.434 μs (1 allocation: 16 bytes)
500105.25f0

julia> @btime ihatehofs(a)
  106.639 μs (1 allocation: 16 bytes)
500105.25f0

```

---

<div class="post-metadata">

**Author:** ![xor0110](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xor0110/32/7926_2.png) [@xor0110](https://discourse.julialang.org/u/xor0110)\
**Post date:** [July 14, 2019, 9:09pm UTC](https://discourse.julialang.org/t/seemingly-unnecessary-code-cruft-in-a-compiled-higher-order-function/26357/3 "2019-07-14T21:09:39Z")

</div>

For large arrays, yes. Like I said, it’s just an extra 2ns, the difference can only be noticed in small inputs.

---

<div class="post-metadata">

**Author:** ![tkoolen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkoolen/32/1603_2.png) [@tkoolen](https://discourse.julialang.org/u/tkoolen)\
**Post date:** [July 14, 2019, 9:26pm UTC](https://discourse.julialang.org/t/seemingly-unnecessary-code-cruft-in-a-compiled-higher-order-function/26357/4 "2019-07-14T21:26:35Z")

</div>

Actually, on my machine the difference is much larger:

```julia
julia> @btime ilovehofs($A)
  63.462 ns (1 allocation: 16 bytes)
508.37314f0

julia> @btime ihatehofs($A)
  32.972 ns (0 allocations: 0 bytes)
508.37314f0

```

Looking at the `code_native`, `ilovehofs` fails to produce code that uses AVX instructions (no `ymm` registers).

My `versioninfo()`:

```julia
Julia Version 1.1.0
Commit 80516ca202 (2019-01-21 21:24 UTC)
Platform Info:
  OS: macOS (x86_64-apple-darwin14.5.0)
  CPU: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.1 (ORCJIT, skylake)

```

What’s yours?

---

<div class="post-metadata">

**Author:** ![tkoolen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkoolen/32/1603_2.png) [@tkoolen](https://discourse.julialang.org/u/tkoolen)\
**Post date:** [July 14, 2019, 9:48pm UTC](https://discourse.julialang.org/t/seemingly-unnecessary-code-cruft-in-a-compiled-higher-order-function/26357/5 "2019-07-14T21:48:14Z")

</div>

On 1.2-rc2, `ilovehofs` _does_ result in AVX instructions and is consequently much closer to `ihatehofs`, but there’s still a 4 ns gap on my machine. I’m mostly just very surprised that on 1.1 `ilovehofs` not only fails to produce AVX instructions, but even results in an allocation! I thought it might be a failure to specialize on the type of `r` so I changed the signature to `myfoldl(r::R, init::T, aa::Vector{T}) where {R, T}`, but that didn’t help. Neither did adding `@inline` or removing the `@simd` annotation; in fact the latter results in downright horrible performance. Very surprising results.

---

<div class="post-metadata">

**Author:** ![tkoolen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkoolen/32/1603_2.png) [@tkoolen](https://discourse.julialang.org/u/tkoolen)\
**Post date:** [July 14, 2019, 10:14pm UTC](https://discourse.julialang.org/t/seemingly-unnecessary-code-cruft-in-a-compiled-higher-order-function/26357/6 "2019-07-14T22:14:58Z")

</div>

On 1.1, it appears that making `A` `const` results in the same performance for both functions. Again, very surprising, as I expected this to be equivalent to just having the `btime` interpolation with `$`.

Edit:  
Ah, d’oh.

```julia
ilovehofs(a::Vector{T}) where T = myfoldl(+, 0f0, A)

```

should be

```julia
ilovehofs(a::Vector{T}) where T = myfoldl(+, 0f0, a) # lower case a

```

---

<div class="post-metadata">

**Author:** ![xor0110](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xor0110/32/7926_2.png) [@xor0110](https://discourse.julialang.org/u/xor0110)\
**Post date:** [July 15, 2019, 11:21am UTC](https://discourse.julialang.org/t/seemingly-unnecessary-code-cruft-in-a-compiled-higher-order-function/26357/7 "2019-07-15T11:21:13Z")

</div>

Oops, sorry for that one!

---

<div class="post-metadata">

**Author:** ![xor0110](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xor0110/32/7926_2.png) [@xor0110](https://discourse.julialang.org/u/xor0110)\
**Post date:** [July 15, 2019, 11:22am UTC](https://discourse.julialang.org/t/seemingly-unnecessary-code-cruft-in-a-compiled-higher-order-function/26357/8 "2019-07-15T11:22:01Z")

</div>

My versioninfo

> Julia Version 1.3.0-DEV.540  
> Commit faefe2ae64\* (2019-07-13 08:34 UTC)  
> Platform Info:  
> OS: Linux (x86\_64-linux-gnu)  
> CPU: Intel(R) Core™ i5-8250U CPU @ 1.60GHz  
> WORD\_SIZE: 64  
> LIBM: libopenlibm  
> LLVM: libLLVM-6.0.1 (ORCJIT, skylake)

---

<div class="post-metadata">

**Author:** ![tkoolen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkoolen/32/1603_2.png) [@tkoolen](https://discourse.julialang.org/u/tkoolen)\
**Post date:** [July 15, 2019, 1:03pm UTC](https://discourse.julialang.org/t/seemingly-unnecessary-code-cruft-in-a-compiled-higher-order-function/26357/9 "2019-07-15T13:03:05Z")

</div>

To be clear, the typo completely explains the performance difference regardless of Julia version; it means that a non-`const` global variable is referred to, which is a performance no-no, [Performance Tips · The Julia Language](https://docs.julialang.org/en/v1/manual/performance-tips/#Avoid-global-variables-1).

---

<div class="post-metadata">

**Author:** ![xor0110](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xor0110/32/7926_2.png) [@xor0110](https://discourse.julialang.org/u/xor0110)\
**Post date:** [July 15, 2019, 3:08pm UTC](https://discourse.julialang.org/t/seemingly-unnecessary-code-cruft-in-a-compiled-higher-order-function/26357/10 "2019-07-15T15:08:53Z")

</div>

Oh, OK, that was basically creating a closure instead of a pure function, is that right? The code seems identical now. Thanks again, now we are all free to love or hate higher-order functions as we wish!
