# The columns and the rows - puzzled about performance

**URL:** <https://discourse.julialang.org/t/the-columns-and-the-rows-puzzled-about-performance/69741>\
**Category:** Performance\
**Tags:** question\
**Created:** [October 14, 2021, 10:46am UTC](https://discourse.julialang.org/t/the-columns-and-the-rows-puzzled-about-performance/69741 "2021-10-14T10:46:58Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Niceno](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/niceno/32/29648_2.png) [@Niceno](https://discourse.julialang.org/u/Niceno)\
**Post date:** [October 14, 2021, 10:46am UTC](https://discourse.julialang.org/t/the-columns-and-the-rows-puzzled-about-performance/69741/1 "2021-10-14T10:46:58Z")

</div>

Dear all,

I have been using Julia for about three weeks now and, to my understanding, she uses a column-major matrix storage system. That would also mean, if I get things right, that loops which access matrix entries in a way that inner-most loops browse over columns (second index, right?) should be more efficient than the other way around.

However, that is not what I observe in my numerical experiments. This fraction of the code shows a typical construct I use in my Julia program:

```
NC = 40000000

bu = ones(3,NC); fc = ones(3,NC); fu = ones(3,NC)
γ1 = 0.73; γ2 = 1.0 -γ1

function F(bu :: Matrix{Float64},
           fc :: Matrix{Float64},
           fu :: Matrix{Float64},
           γ1 :: Float64,
           γ2 :: Float64,
           NT :: Int64)
  @time for i = 1 : NT
    for j = 1 : 3
      bu[j,i] = bu[j,i] + γ1 * fc[j,i] + γ2 * fu[j,i]
    end
  end
end

println("Invoke with: F(bu, fc, fu, γ1, γ2, NC)")

```

Which gives me times in the range of 0.24 seconds. The above fraction of the code uses inner-most loop `j` to browse over rows (first index) so it should be inefficient. However, if I rewrite this piece of code so that inner-most loop browses over columns (second index) like this:

```
NC = 40000000

bu = ones(NC,3); fc = ones(NC,3); fu = ones(NC,3)
γ1 = 0.73; γ2 = 1.0 -γ1

function F(bu :: Matrix{Float64},
           fc :: Matrix{Float64},
           fu :: Matrix{Float64},
           γ1 :: Float64,
           γ2 :: Float64,
           NT :: Int64)
  @time for i = 1 : NT
    for j = 1 : 3
      bu[i,j] = bu[i,j] + γ1 * fc[i,j] + γ2 * fu[i,j]
    end
  end
end

println("Invoke with: F(bu, fc, fu, γ1, γ2, NC)")

```

I am consistently slower by some 20%, i.e. the execution times are in the order of 0.3 s.

What is going on here? Am I confusing columns with rows?

Please let me know what you think?

Cheers

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [October 14, 2021, 10:53am UTC](https://discourse.julialang.org/t/the-columns-and-the-rows-puzzled-about-performance/69741/2 "2021-10-14T10:53:22Z")

</div>

Linking the corresponding [manual section](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-column-major).

---

<div class="post-metadata">

**Author:** ![Niceno](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/niceno/32/29648_2.png) [@Niceno](https://discourse.julialang.org/u/Niceno)\
**Post date:** [October 14, 2021, 11:02am UTC](https://discourse.julialang.org/t/the-columns-and-the-rows-puzzled-about-performance/69741/3 "2021-10-14T11:02:47Z")

</div>

Thanks. So, if the construct:

```julia
  for col = inds, row = inds
    out[row, col] = x[row]
  end

```

is equivalent to:

```julia
  for col = inds
    for row = inds
      out[row, col] = x[row]
    end
  end

```

which is more efficient way to browse, and also consistent with what I observe, **the inner-most loop should go over rows** , not over columns, right?

---

<div class="post-metadata">

**Author:** ![nilshg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nilshg/32/2283_2.png) [@nilshg](https://discourse.julialang.org/u/nilshg)\
**Post date:** [October 14, 2021, 11:14am UTC](https://discourse.julialang.org/t/the-columns-and-the-rows-puzzled-about-performance/69741/4 "2021-10-14T11:14:59Z")

</div>

Unless I always misunderstood it, that’s what column-major implies: a column is stored contiguously in memory, so you want to have subsequent iterations of your loop access elements in the same column as much as possible. You do this by going row-by-row down the first column, then moving to the second column (which is not contiguous in memory to the first column) and going through all elements in that column row-by-row, and so on.

---

<div class="post-metadata">

**Author:** ![Niceno](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/niceno/32/29648_2.png) [@Niceno](https://discourse.julialang.org/u/Niceno)\
**Post date:** [October 14, 2021, 11:16am UTC](https://discourse.julialang.org/t/the-columns-and-the-rows-puzzled-about-performance/69741/5 "2021-10-14T11:16:50Z")

</div>

Neatly explained nilshg, now I get a clear picture 🙂

---

<div class="post-metadata">

**Author:** ![LaurentPlagne](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/laurentplagne/32/10103_2.png) [@LaurentPlagne](https://discourse.julialang.org/u/LaurentPlagne)\
**Post date:** [October 14, 2021, 11:25am UTC](https://discourse.julialang.org/t/the-columns-and-the-rows-puzzled-about-performance/69741/6 "2021-10-14T11:25:09Z")

</div>

Notice that the type annotation does not help for performance (while @inbounds may)

```julia
NC = 40000000

bu = ones(3, NC); fc = ones(3, NC); fu = ones(3, NC)
γ1 = 0.73; γ2 = 1.0 - γ1

function F(bu, fc, fu, γ1, γ2)
    nc,nr=size(bu)
    @inbounds for j = 1:nc
        for i = 1:nr
            bu[i,j] = bu[i,j] + γ1 * fc[i,j] + γ2 * fu[i,j]
        end
    end
end

@time F(bu, fc, fu, γ1, γ2)
@time F(bu, fc, fu, γ1, γ2)
@time F(bu, fc, fu, γ1, γ2)

println("Invoke with: F(bu, fc, fu, γ1, γ2)")

```

---

<div class="post-metadata">

**Author:** ![Niceno](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/niceno/32/29648_2.png) [@Niceno](https://discourse.julialang.org/u/Niceno)\
**Post date:** [October 14, 2021, 11:28am UTC](https://discourse.julialang.org/t/the-columns-and-the-rows-puzzled-about-performance/69741/7 "2021-10-14T11:28:40Z")

</div>

Thanks for the tip!

---

<div class="post-metadata">

**Author:** ![zdenek\_hurak](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zdenek_hurak/32/53118_2.png) [@zdenek\_hurak](https://discourse.julialang.org/u/zdenek_hurak)\
**Post date:** [October 14, 2021, 11:42am UTC](https://discourse.julialang.org/t/the-columns-and-the-rows-puzzled-about-performance/69741/8 "2021-10-14T11:42:25Z")

</div>

Although it seems already sufficiently clarified, you may perhaps still want to have a look at my take [Why column major? - #36 by zdenek\_hurak](https://discourse.julialang.org/t/why-column-major/24374/36)

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [October 14, 2021, 12:05pm UTC](https://discourse.julialang.org/t/the-columns-and-the-rows-puzzled-about-performance/69741/9 "2021-10-14T12:05:29Z")

</div>

For the record, the Wikipedia article [Row- and column-major order](https://en.wikipedia.org/wiki/Row-_and_column-major_order) is a valuable resource on this topic too.
