# Nested static arrays perform worse than nested arrays

**URL:** <https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938>\
**Category:** Performance\
**Tags:** question, package\
**Created:** [November 4, 2021, 8:10am UTC](https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938 "2021-11-04T08:10:39Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![anneJulia](https://avatars.discourse-cdn.com/v4/letter/a/f4b2a3/32.png) [@anneJulia](https://discourse.julialang.org/u/anneJulia)\
**Post date:** [November 4, 2021, 8:10am UTC](https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938/1 "2021-11-04T08:10:39Z")

</div>

I am performing array operations, where I retrieve elements of an array (this array is nested). The functions below are minimal working examples of what I am trying to do.

```julia
using TimerOutputs
using StaticArrays
using StatsBase

const track = TimerOutput()

########## normal array: 

function nestedNormalArray(input)

    output = zeros(20)
    for i in 1:20
        output[i] = input[I][rand(1:5)]
    end
    return output
end

@timeit track "arrayNestedInput" for j in 1:10000000
    
    temp = Vector{Any}(undef,20) 
    for k in 1:20 
        random = digits(rand(1:((2^5)-1)), base=2, pad=5)
        temp[k] = random
    end

    nestedNormalArray(temp)
    
end

########## static array: 

function nestedStaticArray(input)

    output = zeros(MVector{20,Int32})
    for i in 1:20
        output[i] = input[I][rand(1:5)]
    end
    return output
end

@timeit track "staticArrayNestedInput" for j in 1:10000000
    
    temp = Vector{Any}(undef,20) 
    for k in 1:20 
        random = digits(rand(1:((2^5)-1)), base=2, pad=5)
        temp[k] = MVector{5}(random)
    end

    inside = SVector{20}(temp)
    nestedStaticArray(inside)
    
end

```

I used the package TimerOutputs to get the allocated memory and the time both functions needed. Surprisingly the version with the static arrays needed a lot more allocations and time.

```julia
 ─────────────────────────────────────────────────────────────────────────────────
                                          Time Allocations      
                                  ────────────────────── ───────────────────────
         Tot / % measured: 128s / 74.9% 67.5GiB / 100%     

 Section ncalls time %tot avg alloc %tot avg
 ─────────────────────────────────────────────────────────────────────────────────
 staticArrayNestedInput 1 50.1s 52.2% 50.1s 39.2GiB 58.1% 39.2GiB
 arrayNestedInput 1 45.8s 47.8% 45.8s 28.3GiB 41.9% 28.3GiB
 ─────────────────────────────────────────────────────────────────────────────────

```

Does anybody know how to reduce the number of allocations and the time?

---

<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:** [November 4, 2021, 9:12am UTC](https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938/2 "2021-11-04T09:12:08Z")

</div>

You are benchmarking on global variables, and using `Vector{Any}`. That’s basically the two worst things you can do performance-wise 😉

Make sure to put your code in functions, and then benchmark it using the BenchmarkTools package. And avoid `Vector{Any}` at all cost, that will kill performance completely.

Take a look at the performance tips section of the manual: [Performance Tips · The Julia Language](https://docs.julialang.org/en/v1/manual/performance-tips/)

---

<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:** [November 4, 2021, 9:16am UTC](https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938/3 "2021-11-04T09:16:05Z")

</div>

> [@anneJulia](#):
>
> `in = SVector{20}(temp)`

I’m not sure about the consequences, but it looks risky to overwrite `in` 😬

---

<div class="post-metadata">

**Author:** ![anneJulia](https://avatars.discourse-cdn.com/v4/letter/a/f4b2a3/32.png) [@anneJulia](https://discourse.julialang.org/u/anneJulia)\
**Post date:** [November 4, 2021, 9:17am UTC](https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938/4 "2021-11-04T09:17:15Z")

</div>

Right, I am definitely going to change this

---

<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:** [November 4, 2021, 9:20am UTC](https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938/5 "2021-11-04T09:20:58Z")

</div>

> [@anneJulia](#):
>
> `rand = digits(sample(1:((2^5)-1)), base=2, pad=5)`

`rand`, too is a Base function that is better to leave alone here. In fact, you may just use `rand(1:5)` instead of `sample(1:5)`.

---

<div class="post-metadata">

**Author:** ![anneJulia](https://avatars.discourse-cdn.com/v4/letter/a/f4b2a3/32.png) [@anneJulia](https://discourse.julialang.org/u/anneJulia)\
**Post date:** [November 4, 2021, 9:23am UTC](https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938/6 "2021-11-04T09:23:05Z")

</div>

Thank you, also going to change this 😬 😅

---

<div class="post-metadata">

**Author:** ![anneJulia](https://avatars.discourse-cdn.com/v4/letter/a/f4b2a3/32.png) [@anneJulia](https://discourse.julialang.org/u/anneJulia)\
**Post date:** [November 4, 2021, 9:34am UTC](https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938/7 "2021-11-04T09:34:33Z")

</div>

I put my code into functions, so that `temp` and `inside` are no longer global, and I changed `Vector{Any}` into `Vector{Vector{Int64}}` .

```julia
using TimerOutputs
using StaticArrays

const track = TimerOutput()

########## normal array: 

function nestedNormalArray(input)

    output = zeros(20)
    for i in 1:20
        output[i] = input[i][sample(1:5)]
    end
    return output
end

function makeNormalNestedArray()
    temp = Vector{Vector{Int64}}(undef,20) 
    for k in 1:20 
        random = digits(rand(1:((2^5)-1)), base=2, pad=5)
        temp[k] = random
    end
    return temp
end

@timeit track "arrayNestedInput" for j in 1:10000000
    nestedNormalArray(makeNormalNestedArray())
end

########## static array: 

function nestedStaticArray(input)

    output = zeros(MVector{20,Int32})
    for i in 1:20
        output[i] = input[i][sample(1:5)]
    end
    return output
end

function makeNestedStaticArray()

    temp = Vector{Vector{Int64}}(undef,20) 
    for k in 1:20 
        random = digits(rand(1:((2^5)-1)), base=2, pad=5)
        temp[k] = MVector{5}(random)
    end

    inside = SVector{20}(temp)
    return inside 
end

@timeit track "staticArrayNestedInput" for j in 1:10000000
    nestedStaticArray(makeNestedStaticArray())
end

```

And this is the output I get:

```julia
 ─────────────────────────────────────────────────────────────────────────────────
                                          Time Allocations      
                                  ────────────────────── ───────────────────────
         Tot / % measured: 47.9s / 90.4% 88.1GiB / 100%     

 Section ncalls time %tot avg alloc %tot avg
 ─────────────────────────────────────────────────────────────────────────────────
 staticArrayNestedInput 1 27.3s 63.0% 27.3s 59.8GiB 67.9% 59.8GiB
 arrayNestedInput 1 16.0s 37.0% 16.0s 28.3GiB 32.1% 28.3GiB
 ─────────────────────────────────────────────────────────────────────────────────

```

Even more allocations are made.

---

<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:** [November 4, 2021, 9:42am UTC](https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938/8 "2021-11-04T09:42:34Z")

</div>

I’m not sure about the end goal here, but can you do this?

```julia
using StaticArrays, BenchmarkTools

function nestedStaticArray(input)
    output = MVector{20,Int32}(undef)
    for i in eachindex(output)
        output[i] = input[i][rand(1:5)]
    end
    return output
end

temp = rand(SVector{20, SVector{5, Bool}})

jl> @btime nestedStaticArray($temp)
  121.540 ns (1 allocation: 96 bytes)
20-element MVector{20, Int32} with indices SOneTo(20):
 1
 1
 0
 0
 0
 1
 0
 1
 0
 1
 1
 1
 1
 1
 1
 1
 1
 0
 0
 1

```

If it’s all zeros and ones, `output` could also use `Bool`s instead of `Int32`.

---

<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:** [November 4, 2021, 9:46am UTC](https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938/9 "2021-11-04T09:46:49Z")

</div>

> [@DNF](#):
>
> `output[i] = input[i][rand(1:5)]`

It may be nicer to use

```julia
output[i] = rand(input[i])

```

That will randomly select one of the elements of `input[i]`, and not rely on having to hardcode the index range.

And you could, in principle just write the whole thing as

```julia
temp = rand(SVector{20, SVector{5, Bool}})
output = rand.(temp)

```

That would return an `SVector{20, Bool}`, but you could change this around if you like and absolutely need `MVector`s and `Int32`s.

---

<div class="post-metadata">

**Author:** ![anneJulia](https://avatars.discourse-cdn.com/v4/letter/a/f4b2a3/32.png) [@anneJulia](https://discourse.julialang.org/u/anneJulia)\
**Post date:** [November 4, 2021, 9:49am UTC](https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938/10 "2021-11-04T09:49:29Z")

</div>

In my actual code I’m using specific boolean vectors, I don’t use `rand()`, for the MWE I just wanted to give my function different boolean vectors.

---

<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:** [November 4, 2021, 9:52am UTC](https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938/11 "2021-11-04T09:52:24Z")

</div>

> [@anneJulia](#):
>
> In my actual code I’m using specific boolean vectors, I don’t use `rand()`

Not sure I understand. Does this make a difference? `rand(input[i])` will select a random element of `input[i]`, is that not what is wanted? Maybe you can add some more detail to your MWE?

---

<div class="post-metadata">

**Author:** ![anneJulia](https://avatars.discourse-cdn.com/v4/letter/a/f4b2a3/32.png) [@anneJulia](https://discourse.julialang.org/u/anneJulia)\
**Post date:** [November 4, 2021, 10:03am UTC](https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938/12 "2021-11-04T10:03:07Z")

</div>

I will try to give my MWE more detail. In general, my goal is to give my function `nestedStaticArray(input, idx)` a boolean vector `input` and a nested vector `idx` which has (inside each nested vector) the indexes which I want to retrieve from `input` with a for loop.

---

<div class="post-metadata">

**Author:** ![anneJulia](https://avatars.discourse-cdn.com/v4/letter/a/f4b2a3/32.png) [@anneJulia](https://discourse.julialang.org/u/anneJulia)\
**Post date:** [November 4, 2021, 10:50am UTC](https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938/13 "2021-11-04T10:50:07Z")

</div>

```julia
using TimerOutputs
using StaticArrays

const track = TimerOutput()

function nestedStaticArray(input, idx, reference)
    output = zeros(MVector{20,Int32})
    for i in 1:20
        tempi = input[idx[i]]
        t = 0
        for u in 0:(length(tempi)-1)
            t = t+ 2^u * tempi[u+1]
        end
        output[i] = reference[t+1] 
    end
    return output
end

function makeNestedStaticArray()
    temp = Vector{Vector{Int64}}(undef,20) 
    for k in 1:20 
        random = rand(1:5, 5)
        temp[k] = MVector{5}(random)
    end
    inside = SVector{20}(temp)
    return inside 
end

function makeReferenceStatic()
    output = zeros(MVector{2^5,Int32})
    for i in 1:(2^5)
        if i % 2 == 0
            output[i] = 1
        else
            output[i] = 0
        end
    end
    return output
end

@timeit track "staticArrayNestedInput" for j in 1:10000000
    nestedStaticArray(digits(rand(1:((2^20)-1)), base=2, pad=20),makeNestedStaticArray(), makeReferenceStatic() )
end

```

I am giving my function `nestedStaticArray` a `input` vector of length 20 (boolean), in the ith iteration of the for loop I want to create a smaller boolean vector `tempi` consisting only of a subarray of `input`, namely `input` at the indexes of `idx[i]`, the boolean vector tempi is then turned into a number of base 10 and `output` now gets the value of `reference` at this computed index. (The function `makeReferenceStatic()` is normally more complicated, but for the sake of this MWE I constructed it this way, and I also don’t do this: `digits(rand(1:((2^20)-1)), base=2, pad=20)`, but I give my function a different boolean vector of length 20 each time I call the function)

```julia
 ─────────────────────────────────────────────────────────────────────────────────
                                          Time Allocations      
                                  ────────────────────── ───────────────────────
         Tot / % measured: 62.0s / 91.2% 94.2GiB / 100%     

 Section ncalls time %tot avg alloc %tot avg
 ─────────────────────────────────────────────────────────────────────────────────
 staticArrayNestedInput 1 56.5s 100% 56.5s 94.2GiB 100% 94.2GiB
 ─────────────────────────────────────────────────────────────────────────────────

```

---

<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:** [November 4, 2021, 8:00pm UTC](https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938/14 "2021-11-04T20:00:39Z")

</div>

Not sure if this does the same as yours, but it’s faster at least:

```julia
makeref() = iseven.(SOneTo(32))
rand5() = SVector{5}(rand(1:5) for _ in 1:5)
makenested() = SVector{20}(rand5() for _ in 1:20)

function nestedsarray(input, ind, ref)
    output = MVector{20, Bool}(undef)
    for i in eachindex(ind)
        tempi = input[ind[i]]
        t, u2 = 0, 1
        for u in eachindex(tempi)
            t += u2 * tempi[u]
            u2 *= 2
        end
        output[i] = ref[t+1]
    end
    return SVector{20,Bool}(output)
end

nestedsarray(rand(SVector{20,Bool}), makenested(), makeref())

```

I recommend using BenchmarkTools instead of running your code 10 million times:

```julia
@btime nestedsarray(rand(SVector{20,Bool}), makenested(), makeref())
  1.000 μs (1 allocation: 48 bytes)

```

---

<div class="post-metadata">

**Author:** ![anneJulia](https://avatars.discourse-cdn.com/v4/letter/a/f4b2a3/32.png) [@anneJulia](https://discourse.julialang.org/u/anneJulia)\
**Post date:** [November 12, 2021, 11:02am UTC](https://discourse.julialang.org/t/nested-static-arrays-perform-worse-than-nested-arrays/70938/15 "2021-11-12T11:02:24Z")

</div>

Thank you 😌  
I will use `@btime` in the future.
