# Mysterious performance degradation involving similar(), view() and recursion

**URL:** <https://discourse.julialang.org/t/mysterious-performance-degradation-involving-similar-view-and-recursion/6977>\
**Category:** General Usage\
**Tags:** bug, performance\
**Created:** [November 9, 2017, 9:44am UTC](https://discourse.julialang.org/t/mysterious-performance-degradation-involving-similar-view-and-recursion/6977 "2017-11-09T09:44:38Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)\
**Post date:** [November 9, 2017, 9:44am UTC](https://discourse.julialang.org/t/mysterious-performance-degradation-involving-similar-view-and-recursion/6977/1 "2017-11-09T09:44:38Z")

</div>

I was writing a function that involves the usage of similar() view() and recursion and below is the best MWE I can come up with. My original function is more complex.

I am running this on Julia 0.6.1 on Windows 10 64bit.

The only difference between `abc()` and `abc2()` below is the lines

```julia
by_sim = similar(by)
val1 = similar(val)

```

they are either placed before the if statement `if hi <= 50; return Dict(by[1] => val[1]);` or after with `abc()` being the former. The number of memory allocations is double in the `abc2()` vs `abc()` which I thought was odd. Actually this example doesn’t give much difference in timing, but allocation is a lot higher.

In my more complicated function, a similar change actually caused a 10x drop in timings. So I wanted to report this issue.

```julia
by = rand(Int8(1):Int8(100), 10_000_000)
val = Int32.(similar(by))

function abc(by, val; j = 1)
    hi = length(by)
    by_sim = similar(by)
    val1 = similar(val)
    if hi <= 50; return Dict(by[1] => val[1]); end

    by_sim[rand(1:hi,hi)] = by[rand(1:hi,hi)]
    val1[rand(1:hi,hi)] = val[rand(1:hi,hi)]

    if j == 2
        return Dict(by_sim[1] => val1[1])
    else
        ii = unique(Int.(round.(collect(linspace(0,hi)))))
        return reduce(merge, [abc(view(by_sim,i1+1:i2), view(val1,i1+1:i2); j = j + 1) for (i1,i2) in zip(ii[1:end-1],ii[2:end])])
    end
end

using Compat, BenchmarkTools
@btime abc(by, val)

function abc2(by, val; j = 1)
    hi = length(by)
    if hi <= 50; return Dict(by[1] => val[1]); end
    by_sim = similar(by)
    val1 = similar(val)

    by_sim[rand(1:hi,hi)] = by[rand(1:hi,hi)]
    val1[rand(1:hi,hi)] = val[rand(1:hi,hi)]

    if j == 2
        return Dict(by_sim[1] => val1[1])
    else
        ii = unique(Int.(round.(collect(linspace(0,hi)))))
        return reduce(merge, [abc2(view(by_sim,i1+1:i2), view(val1,i1+1:i2); j = j + 1) for (i1,i2) in zip(ii[1:end-1],ii[2:end])])
    end
end

@btime abc2(by, val)

```

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [November 9, 2017, 11:07am UTC](https://discourse.julialang.org/t/mysterious-performance-degradation-involving-similar-view-and-recursion/6977/2 "2017-11-09T11:07:15Z")

</div>

`similar` allocates output arrays, but your `if hi <= 50` line allows for a quick return that doesn’t make use of `by_sim` or `val1`. By placing the `similar` lines after the `if`, you can skip the allocation when that condition holds.

---

<div class="post-metadata">

**Author:** ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)\
**Post date:** [November 9, 2017, 11:09am UTC](https://discourse.julialang.org/t/mysterious-performance-degradation-involving-similar-view-and-recursion/6977/3 "2017-11-09T11:09:01Z")

</div>

The strange thing is by placing it after it’s slower.

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [November 9, 2017, 1:23pm UTC](https://discourse.julialang.org/t/mysterious-performance-degradation-involving-similar-view-and-recursion/6977/4 "2017-11-09T13:23:26Z")

</div>

Not on my machine.

```julia
julia> @btime abc(by, val)
  2.751 s (1542 allocations: 801.21 MiB)

julia> @btime abc2(by, val)
  2.603 s (3345 allocations: 801.27 MiB)

```

I didn’t look into the allocations, though.

---

<div class="post-metadata">

**Author:** ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)\
**Post date:** [November 9, 2017, 7:37pm UTC](https://discourse.julialang.org/t/mysterious-performance-degradation-involving-similar-view-and-recursion/6977/5 "2017-11-09T19:37:57Z")

</div>

one is 1542 allocation the other being 3000+ allocations. It’s in the  
outpur you showed and I got the same.

This is the strange thing. In my more complicated example doing similar after the `if` statement actually _increases_ running time! That’s why it’s strange and unintuitive.
