# Performance issue with Vectors vs. 1 Column Matrices

**URL:** https://discourse.julialang.org/t/performance-issue-with-vectors-vs-1-column-matrices/121240
**Category:** Performance
**Tags:** question, performance, benchmarktools
**Created:** [October 13, 2024, 2:51am UTC](https://discourse.julialang.org/t/performance-issue-with-vectors-vs-1-column-matrices/121240 "2024-10-13T02:51:53Z")
**Posts on this page:** 9
**Page:** 1

<div class="post-metadata">

### Author: ![gideonsimpson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gideonsimpson/32/1928_2.png) [@gideonsimpson](https://discourse.julialang.org/u/gideonsimpson)
#### Post date: [October 13, 2024, 2:51am UTC](https://discourse.julialang.org/t/performance-issue-with-vectors-vs-1-column-matrices/121240/1 "2024-10-13T02:51:53Z")

</div>

Buried inside a for loop of mine, I have a function which, morally, looks like:

```julia
function f!(a,b)
  @. a = b;
  a
end

```

It gets used inside the for loop like:

```julia
# construct vector x of length nx
# construct vector y of length ny > nx
for i in 1:iters
# code
rows = sample(1:ny, nx,replace = false); # grab random rows of y;
f!(x, y[rows]);
end

```

The above code works without issue. However, there are some cases where, instead of `x` and `y` being vectors, they are matrices with the same number of columns. The equivalent code is then:

```julia
# construct matrix x of size (nx,d)
# construct vector y of size (ny,d)
for i in 1:iters
# code
rows = sample(1:ny, nx,replace = false); # grab random rows of y;
f!(x, y[rows,:]);
end

```

and notice that I have had to use `y[rows,;]`. Now, I could use this in the vector case, but I take a performance hit:

```julia
Random.seed!(100);
x = zeros(100);
y = randn(1000);
rows = sample(1:1000, 100, replace=false);
@btime f!(x,y[rows]);

  124.491 ns (2 allocations: 928 bytes)

@btime f!(x,y[rows,:]);

  320.127 ns (2 allocations: 944 bytes)

```

Also, if I create `x` and `y` as `zeros(100,1)` and `randn(1000,1)`, I also take the performance hit.

The case where I am only working with vectors is common enough for me that I don’t want to take this performance hit. To avoid the hit now, I have two versions of my outer function (which has the aforementioned for loop), and one is for the vector case and one is for the matrix case via multiple dispatch. But it seems really silly to have that when the only distinction is `y[rows]` and `y[rows,:]`. Is there a cleaner way to reconcile this?

---

<div class="post-metadata">

### Author: ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)
#### Post date: [October 13, 2024, 6:02am UTC](https://discourse.julialang.org/t/performance-issue-with-vectors-vs-1-column-matrices/121240/2 "2024-10-13T06:02:43Z")

</div>

I think multiple dispatch is your answer here. There might be variants of `selectdim` which would work for a list of indices but I’m not aware of them.

About performance itself, two little tips:

- don’t forget to [interpolate global variables](https://juliaci.github.io/BenchmarkTools.jl/stable/manual/#Interpolating-values-into-benchmark-expressions) when you use BenchmarkTools.jl
- try array views to avoid allocations (not always faster, see below)

```julia
const AV = AbstractVector
const AM = AbstractMatrix

partialcopy!(x::AV, y::AV, rows) = x .= y[rows]
partialcopy!(x::AM, y::AM, rows) = x .= y[rows, :]

partialcopy2!(x::AV, y::AV, rows) = x .= @view y[rows]
partialcopy2!(x::AM, y::AM, rows) = x .= @view y[rows, :]

```

Setup code:

```julia
using BenchmarkTools, StatsBase
x, X = zeros(100), zeros(100, 1)
y, Y = ones(1000), ones(1000, 1)
rows = sample(1:1000, 100, replace=false)

```

Results:

```julia
julia> @btime partialcopy!($x, $y, $rows);
  211.100 ns (1 allocation: 896 bytes)

julia> @btime partialcopy2!($x, $y, $rows);
  113.900 ns (0 allocations: 0 bytes)

julia> @btime partialcopy!($X, $Y, $rows);
  248.100 ns (1 allocation: 896 bytes)

julia> @btime partialcopy2!($X, $Y, $rows);
  332.664 ns (0 allocations: 0 bytes)

```

Interestingly, the array views speed things up for vectors and slow them down for matrices. I think you’d actually be better served by writing out the loop explicitly here.

---

<div class="post-metadata">

### Author: ![gideonsimpson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gideonsimpson/32/1928_2.png) [@gideonsimpson](https://discourse.julialang.org/u/gideonsimpson)
#### Post date: [October 13, 2024, 6:43pm UTC](https://discourse.julialang.org/t/performance-issue-with-vectors-vs-1-column-matrices/121240/3 "2024-10-13T18:43:47Z")

</div>

Inspired by this, I came up the following variation which also seems to accomplish the task:

```julia
function f!(a, b)
    @. a = b;
    a
end
function extract_rows(a::TA, rows) where {TA<:AbstractVector}
    return @view a[rows]
end

function extract_rows(a::TA, rows) where {TA<:AbstractMatrix}
    return @view a[rows,:]
end

Random.seed!(100);
nx = 100;
ny = 1000;
x = zeros(nx);
y = randn(ny);
X = x[:, :];
Y = y[:, :];
rows = sample(1:ny, nx, replace=false);
@btime f!($(x), extract_rows($(y), $(rows)));
@btime f!($(X), extract_rows($(Y), $(rows)));

  81.176 ns (0 allocations: 0 bytes)
  123.551 ns (0 allocations: 0 bytes)

```

Thoughts?

---

<div class="post-metadata">

### Author: ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)
#### Post date: [October 13, 2024, 6:54pm UTC](https://discourse.julialang.org/t/performance-issue-with-vectors-vs-1-column-matrices/121240/4 "2024-10-13T18:54:47Z")

</div>

The reason you’re getting better times is that you’re removing the time it takes to compute `extract_rows` from the benchmark (I think). If I understand correctly, BenchmarkTools only benchmarks the outer function, so to speak.

---

<div class="post-metadata">

### Author: ![gideonsimpson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gideonsimpson/32/1928_2.png) [@gideonsimpson](https://discourse.julialang.org/u/gideonsimpson)
#### Post date: [October 13, 2024, 6:58pm UTC](https://discourse.julialang.org/t/performance-issue-with-vectors-vs-1-column-matrices/121240/5 "2024-10-13T18:58:37Z")

</div>

Is your assessment on timing the same for this?

```julia
function g!(a,b,rows)
    f!(a,extract_rows(b, rows))
    a
end

@btime g!($(x), $(y), $(rows));
@btime g!($(X), $(Y), $(rows));
  82.361 ns (0 allocations: 0 bytes)
  124.780 ns (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

### Author: ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)
#### Post date: [October 13, 2024, 7:07pm UTC](https://discourse.julialang.org/t/performance-issue-with-vectors-vs-1-column-matrices/121240/6 "2024-10-13T19:07:28Z")

</div>

I am pretty sure this one is the correct way to benchmark, so I’m very unsettled that it gives the exact same results ^^

---

<div class="post-metadata">

### Author: ![gideonsimpson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gideonsimpson/32/1928_2.png) [@gideonsimpson](https://discourse.julialang.org/u/gideonsimpson)
#### Post date: [October 13, 2024, 7:12pm UTC](https://discourse.julialang.org/t/performance-issue-with-vectors-vs-1-column-matrices/121240/7 "2024-10-13T19:12:28Z")

</div>

I’m just happy that based on your comments I’ve got a viable solution to my code:)

---

<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: [October 13, 2024, 7:19pm UTC](https://discourse.julialang.org/t/performance-issue-with-vectors-vs-1-column-matrices/121240/8 "2024-10-13T19:19:54Z")

</div>

> [@gideonsimpson](#):
>
> ```julia
> function f!(a, b)
> @. a = b;
> x
> end
> 
> ```

Is the returned `x` a typo?

---

<div class="post-metadata">

### Author: ![gideonsimpson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gideonsimpson/32/1928_2.png) [@gideonsimpson](https://discourse.julialang.org/u/gideonsimpson)
#### Post date: [October 13, 2024, 7:36pm UTC](https://discourse.julialang.org/t/performance-issue-with-vectors-vs-1-column-matrices/121240/9 "2024-10-13T19:36:38Z")

</div>

Yes. Edited
