# fusing not faster

**URL:** https://discourse.julialang.org/t/fusing-not-faster/8696
**Category:** Performance
**Tags:** broadcast
**Created:** [January 30, 2018, 3:32pm UTC](https://discourse.julialang.org/t/fusing-not-faster/8696 "2018-01-30T15:32:26Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![mvhulten](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mvhulten/32/2444_2.png) [@mvhulten](https://discourse.julialang.org/u/mvhulten)
#### Post date: [January 30, 2018, 3:32pm UTC](https://discourse.julialang.org/t/fusing-not-faster/8696/1 "2018-01-30T15:32:26Z")

</div>

I just read about [fusing and broadcasting](https://docs.julialang.org/en/stable/manual/functions/#man-vectorized-1). I understood that fusing is faster than other approaches. But I have this code:

```julia
julia> a = rand(1, 10000)
1×10000 Array{Float64,2}:
 0.975847 0.427092 0.480224 … 0.469955 0.950595 0.628602

julia> n = 100
100

julia> dt = zeros(n);

julia> for i=1:n
           t0=time(); sin.(cos.(a)); dt[i]=time()-t0
       end

julia> mean(dt)
0.0006426692008972168

julia> for i=1:n
           t0=time(); sin.(identity(cos.(a))); dt[i]=time()-t0
       end

julia> mean(dt)
0.00042889833450317384

```

Apparently when I don’t fuse, by just adding the identity function in the middle, I get better results.

Since I’m not sure if I understand everything, I did not report a bug; I rather first have these questions:

- Am I actually fusing with `sin.(cos.(a))` and _not_ with `sin.(identity(cos.(a)))`?
- Are there simple examples that would show that fusing is efficient than not to?

---

<div class="post-metadata">

### Author: ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)
#### Post date: [January 30, 2018, 3:34pm UTC](https://discourse.julialang.org/t/fusing-not-faster/8696/2 "2018-01-30T15:34:13Z")

</div>

Don’t time in the global scope. Put it in a function. What you’re actually timing is that it takes longer to compile the fused function.

---

<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: [January 30, 2018, 3:37pm UTC](https://discourse.julialang.org/t/fusing-not-faster/8696/3 "2018-01-30T15:37:31Z")

</div>

See also [GitHub - JuliaCI/BenchmarkTools.jl: A benchmarking framework for the Julia language](https://github.com/JuliaCI/BenchmarkTools.jl#quick-start).

---

<div class="post-metadata">

### Author: ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)
#### Post date: [January 30, 2018, 4:09pm UTC](https://discourse.julialang.org/t/fusing-not-faster/8696/4 "2018-01-30T16:09:16Z")

</div>

> [@mvhulten](#):
>
> a = rand(1, 10000)

You don’t need to put it in a function, but use BenchmarkTools:

```julia
julia> using BenchmarkTools

julia> a = rand(10000); # this is more natural than rand(1, 10000)

julia> @btime sin.(cos.($a));
  243.721 μs (2 allocations: 78.20 KiB)

julia> @btime sin.(identity(cos.($a)));
  212.915 μs (4 allocations: 156.41 KiB)

```

Surprisingly, I find that not fusing _is actually_ faster.

---

<div class="post-metadata">

### Author: ![Seif\_Shebl](https://avatars.discourse-cdn.com/v4/letter/s/eada6e/32.png) [@Seif\_Shebl](https://discourse.julialang.org/u/Seif_Shebl)
#### Post date: [January 30, 2018, 4:09pm UTC](https://discourse.julialang.org/t/fusing-not-faster/8696/5 "2018-01-30T16:09:22Z")

</div>

If you time it properly in a function as others have stated, you can observe they are essentially the same:

```
function test(N)
	a = rand(1, N)
	n = 100
	dt = zeros(n)
	for i = 1:n
		t0 = time();
		sin.(cos.(a));
		dt[i] = time()-t0
	end
	println(mean(dt))

	for i = 1:n
           t0 = time();
		   sin.(identity(cos.(a)));
		   dt[i] = time()-t0
    end
	println(mean(dt))
end

[test(10000) for i in 1:3]

julia>
  0.00023000001907348634
  0.0002499985694885254
  0.00023000001907348634
  0.00023999929428100586
  0.00023000001907348634
  0.00023999929428100586

```

And using `BenchmarkTools` I can replicate the observation of @DNF:

```
using BenchmarkTools
function test(N)
	a = rand(1, N)
	@btime sin.(cos.($a))
    @btime sin.(identity(cos.($a)))
end

julia> test(10000)
  215.279 μs (2 allocations: 78.20 KiB)
  186.798 μs (4 allocations: 156.41 KiB)

```

---

<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: [January 30, 2018, 4:29pm UTC](https://discourse.julialang.org/t/fusing-not-faster/8696/6 "2018-01-30T16:29:27Z")

</div>

There might be enough overhead having to swap between evaluating two different functions, over evaluating the same one (computers are good at doing the same thing over and over) that the non-fused beats out the fused one here.

Also, FWIW, on 0.7 fused vs unfused:

```julia
julia> @btime fs($a);
  178.222 μs (2 allocations: 78.20 KiB)

julia> @btime f_unfused($a);
  185.016 μs (4 allocations: 156.41 KiB)

```

`sin` and `cos` are now defined in julia so that might have changed stuff.

---

<div class="post-metadata">

### Author: ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)
#### Post date: [January 30, 2018, 4:43pm UTC](https://discourse.julialang.org/t/fusing-not-faster/8696/7 "2018-01-30T16:43:40Z")

</div>

More depth. Julia’s “unit of compilation” is a function. It compiles at each function call (at the first time it’s called). Every time a broadcast statement is found, it bounds a new function, compiles that anonymous function, and calls `broadcast(f,...)` where `f` is that anonymous function. So in the global scope it’s going to be measuring this compilation time, while in a function it will happen only the first time. Even `@btime`’s scope seems

```julia
using BenchmarkTools

function f(a)
    sin.(cos.(a))
end

function f2(a)
    sin.(identity(cos.(a)))
end

a = zeros(100)

@time f(a)
@time f2(a)
@time f(a)
@time f2(a)

@btime f($a)
@btime f2($a)

```

gives me

```julia
  0.017187 seconds (15.18 k allocations: 688.813 KiB)
  0.012623 seconds (3.48 k allocations: 144.208 KiB)
  0.000004 seconds (5 allocations: 1.031 KiB)
  0.000004 seconds (6 allocations: 1.906 KiB)
  1.142 μs (1 allocation: 896 bytes)
  1.171 μs (2 allocations: 1.75 KiB)

```

You can see the compilation in the timing in the first call, and the subsequent calls are too fast to be timed with `@time` so `@btime` is used (uses the minimum over a bunch of runs). You can play with seeing how this specific case scales, but you’ll see they are always pretty much the same or fusion is faster.

But fusion really makes more sense when you have pre-allocated output.

```julia
using BenchmarkTools

function f(b,a)
    b .= sin.(cos.(a))
end

function f2(b,a)
    b .= sin.(identity(cos.(a)))
end

a = rand(1000000000)
b = similar(a)

@btime f($b,$a)
@btime f2($b,$a)

  23.560 s (0 allocations: 0 bytes)
  22.194 s (2 allocations: 7.45 GiB)

```

That said, the non-fusing form is surprisingly good here so there may be some optimization going on.

But in real codes you will notice a difference because you see that the non-fusing form is allocating 7.45 GiB. In a real code, that will cause the GC to be hit. In `@btime`, it GCs outside of the function call so it’s not in the timing.

Edit: this computation may be compute bound enough that allocating the vector just doesn’t even matter.

---

<div class="post-metadata">

### Author: ![mvhulten](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mvhulten/32/2444_2.png) [@mvhulten](https://discourse.julialang.org/u/mvhulten)
#### Post date: [January 30, 2018, 5:20pm UTC](https://discourse.julialang.org/t/fusing-not-faster/8696/8 "2018-01-30T17:20:52Z")

</div>

Thank you all for these useful explanations!

If I use `@btime` from `BenchmarkTools`, I indeed get very small differences between fusing or not, very similar to those in your examples.
