# Understanding performance of vectors

**URL:** <https://discourse.julialang.org/t/understanding-performance-of-vectors/107455>\
**Category:** Performance\
**Created:** [December 11, 2023, 4:07pm UTC](https://discourse.julialang.org/t/understanding-performance-of-vectors/107455 "2023-12-11T16:07:35Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![D-Se](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/d-se/32/205391_2.png) [@D-Se](https://discourse.julialang.org/u/D-Se)\
**Post date:** [December 11, 2023, 4:07pm UTC](https://discourse.julialang.org/t/understanding-performance-of-vectors/107455/1 "2023-12-11T16:07:35Z")

</div>

I have created a toy package, [TicTacToes.jl](https://github.com/D-Se/TicTacToes.jl/tree/main) to try and write and package some performant Julia code.

When trying to write an example on how to use make a (bad) agent play itself, I didn’t understand the performance of one approach:

```julia
using TicTacToes
function self_play()
    g = TicTacToe()
    while g.state == RUN ai!(g) end
    g.state
end

n = 100_000_000
@time res = [self_play() for i in 1:n] # +- 23 sec on my PC

struct GameVec <: AbstractVector{Int} len::Int end
Base.size(g::GameVec) = (g.len,)
Base.getindex(::GameVec, i::Int) = self_play()
@time res2 = GameVec(n) # near instant (??)

```

**Question** : Why is `GameVec` so much faster than a simple comprehension? Where can I learn more on what is happening under the hood?

Where I’m stuck: calling `code_native` and friends on `GameVec` just shows it returns its argument. I can’t find where / how the logic in `self_play` is run.

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [December 11, 2023, 4:26pm UTC](https://discourse.julialang.org/t/understanding-performance-of-vectors/107455/2 "2023-12-11T16:26:08Z")

</div>

It looks like you are comparing two very different things, e.g. the first timing is for numerous calls of `self_play()` and the second timing merely constructs an instance of type `GameVec`, which does not call `self_play()` at all.

I assume that the performance here is due to the runtime of `self_play` itself, and calling it 100 million times is also quite a bit, even for a very fast function.

For general performance tips, consider the links in [Optimizing your code (modernjuliaworkflows.github.io)](https://modernjuliaworkflows.github.io/pages/optimizing/optimizing/), in particular the performance tips section and maybe profiling in VS code.

---

<div class="post-metadata">

**Author:** ![D-Se](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/d-se/32/205391_2.png) [@D-Se](https://discourse.julialang.org/u/D-Se)\
**Post date:** [December 11, 2023, 4:54pm UTC](https://discourse.julialang.org/t/understanding-performance-of-vectors/107455/3 "2023-12-11T16:54:13Z")

</div>

Definitely comparing two different things here. I am quite confused what thing `GameVec` creates here, by the `@time` it takes, it doesn’t do much. My confusion comes from the results as I print `res2`.

```julia
res
#> 10000000-element Vector{GameState}:
#> X::GameState = 1
#> DRAW::GameState = 3
#> O::GameState = 2

```

Yet, when it `res2` prints, it does print a game state (an integer from 1 to 3). The state field of the `TicTacToe` is initially always 0, so _something_ happens, but I don’t know how to find out what happens.

```julia
res2
#> 10000000-element GameVec:
#> X::GameState = 1 # these may change when `res2` is printed
#> O::GameState = 2 
#> X::GameState = 1

res2 # recalling the same thing, directly afterward
#> 10000000-element GameVec:
#> DRAW::GameState = 3
#> X::GameState = 1
#> X::GameState = 1

```

However, what `res2` prints, changes on different prints. Each access, i.e. `res2[1]` comes with the same computational cost as 1 run of `self_play()` + some extra.

Then the question, I suppose, is what those results in the `GameVec` are, something similar to `undef` for other vectors?

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [December 11, 2023, 6:08pm UTC](https://discourse.julialang.org/t/understanding-performance-of-vectors/107455/4 "2023-12-11T18:08:10Z")

</div>

`GameVec(n)` is more analogous to the `1:n` part, so a fair comparison is `game1 = GameVec(n); @time res2 = [game1[i] for i in eachindex(game1)]`. `game1[i]` calls `getindex`; `eachindex` calls `size`.

---

<div class="post-metadata">

**Author:** ![D-Se](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/d-se/32/205391_2.png) [@D-Se](https://discourse.julialang.org/u/D-Se)\
**Post date:** [December 11, 2023, 7:24pm UTC](https://discourse.julialang.org/t/understanding-performance-of-vectors/107455/5 "2023-12-11T19:24:57Z")

</div>

Thanks - that makes sense, and answers the performance part of the question.

I’m still a bit confused how/why `SomeVec(5)` in this codeblock prints 40x “I’m running!” (i.e. something is still ran), but at least I know to not see these as values as equal to the ones in the regular loop/comprehension.

```julia
using Random
mutable struct A a::Float64 end
step!(x::A) = x.a += rand()

function run()
    println("I'm running!")
    t = A(1.0)
    while t.a < 2.0 step!(t) end
    t.a
end

Random.seed!(1)
@time res = [run() for i in 1:5]

struct SomeVec <: AbstractVector{Float64} len::Int end
Base.size(s::SomeVec) = (s.len,)
Base.getindex(::SomeVec, i::Int) = run()

init = SomeVec(5) # run() prints stuff 40x
Random.seed!(1)
@time res2 = [init[i] for i in eachindex(init)]

res == res2 # true

```

---

<div class="post-metadata">

**Author:** ![greg\_plowman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/greg_plowman/32/8100_2.png) [@greg\_plowman](https://discourse.julialang.org/u/greg_plowman)\
**Post date:** [December 11, 2023, 9:03pm UTC](https://discourse.julialang.org/t/understanding-performance-of-vectors/107455/6 "2023-12-11T21:03:12Z")

</div>

> [@D-Se](#):
>
> I’m still a bit confused how/why `SomeVec(5)` in this codeblock prints 40x “I’m running!”

Because `getindex` is used in displaying your `SomeVec` object in the REPL.  
You have defined `SomeVec` as a subtype of `AbstractVector`, so it “inherits” lots of functionality including how it is displayed.

You can use a semi-colon ; to suppress printing.

Or you can provide explicit `show` methods for your type:

```julia
Base.show(io::IO, s::SomeVec) = print(io, typeof(s), "(", s.len, ")")
Base.show(io::IO, ::MIME"text/plain", s::SomeVec) = print(io, typeof(s), "(", s.len, ")")

```

See manual pages:  
[I/O and Network · The Julia Language](https://docs.julialang.org/en/v1/base/io-network/#Base.show-Tuple%7BIO,%20Any%7D)  
[I/O and Network · The Julia Language](https://docs.julialang.org/en/v1/base/io-network/#Base.show-Tuple%7BIO,%20Any%7D)

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [December 11, 2023, 11:10pm UTC](https://discourse.julialang.org/t/understanding-performance-of-vectors/107455/7 "2023-12-11T23:10:52Z")

</div>

> [@D-Se](#):
>
> why `SomeVec(5)` in this codeblock prints 40x “I’m running!”

> [@greg\_plowman](#):
>
> Because `getindex` is used in displaying your `SomeVec` object in the REPL.

More exact reason is that `Base.alignment` iterates the rows of a vector or matrix to find the space needed to print all the elements in an aligned column, then `Base.print_matrix_row` iterates again to print. Each iteration indexes a few times, the exact number varying by version, so it’s not good to print while indexing. I’d separate the printing step from the element computation, and write a method (`function (s::SomeVec)(i::Int)...`) for `SomeVec` instances so a just-as-concise call `init(i)` prints while indexing `init[i]` doesn’t.
