# Why is this simple function twice as slow as its Python version

**URL:** <https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004>\
**Category:** Performance\
**Tags:** question\
**Created:** [April 10, 2021, 6:58pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004 "2021-04-10T18:58:14Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Semi](https://avatars.discourse-cdn.com/v4/letter/s/a698b9/32.png) [@Semi](https://discourse.julialang.org/u/Semi)\
**Post date:** [April 10, 2021, 6:58pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/1 "2021-04-10T18:58:14Z")

</div>

I’m in the planing stages for writing a piece of scientific software and I would like to use Julia for it, but I’m surprised how much slower Julia is from Python when tested on this simple code:

```julia
function test(n::Int64,k::Int64,tmp2::Array{Float64,2})
    for i in 1:k
        t = rand(n, n) 
        for j in 1:k
            tmp1 = rand(n, n)
            tmp2[(i-1)*n+1:i*n,(j-1)*n+1:j*n] = t*tmp1
        end
    end
end;

```

The corresponding python code is

```julia
def test(n,k,tmp2):
    for i in range(k):
        t = rand(n, n) 
        for j in range(k):
            tmp1 = rand(n, n)
            tmp2[i*n:(i+1)*n,j*n:(j+1)*n] = t@tmp1

```

When tested with, for example,

```julia
n = 300
k = 30
tmp2=zeros((n*k,n*k))

```

Julia needs more than a second

> @time test(n,k,tmp2)  
> 1.122870 seconds (3.67 k allocations: 1.227 GiB, 15.44% gc time)  
> while python is 30% faster  
> %%timeit  
> test(n,k,tmp2)  
> 853 ms ± 7.68 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

When testing the same function but with  
`tmp2[(i-1)*n+1:i*n,(j-1)*n+1:j*n] = t*tmp1`  
changed to some element-wise operation on the array, Julia is much faster than python, so I suppose this has to do with matrix operations.

I’m using Julia 1.4 on a Macbook pro. I tried switching from openblas to mkl but this did not improved the speed of Julia.

I would be thankful for any hint what I’m doing wrong, or Julia indeed is so slow when dealing with lots of matrix operations. 🙂

---

<div class="post-metadata">

**Author:** ![alastair-marshall](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alastair-marshall/32/21418_2.png) [@alastair-marshall](https://discourse.julialang.org/u/alastair-marshall)\
**Post date:** [April 10, 2021, 7:03pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/2 "2021-04-10T19:03:20Z")

</div>

Hey there!

Have you tried running the Julia code more than once? The first time Julia runs a function it’ll compile a fast version of it, you might have timed how long it takes to compile rather than just the function!

---

<div class="post-metadata">

**Author:** ![dfdx](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dfdx/32/120_2.png) [@dfdx](https://discourse.julialang.org/u/dfdx)\
**Post date:** [April 10, 2021, 7:06pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/3 "2021-04-10T19:06:57Z")

</div>

```julia
t*tmp1

```

In Python, it’s an elementwise multiplication, while in Julia it’s matrix-by-matrix multiplication.

---

<div class="post-metadata">

**Author:** ![Semi](https://avatars.discourse-cdn.com/v4/letter/s/a698b9/32.png) [@Semi](https://discourse.julialang.org/u/Semi)\
**Post date:** [April 10, 2021, 7:09pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/4 "2021-04-10T19:09:49Z")

</div>

Good catch!

But with `t@tmp1`, python is still quite a bit faster  
`853 ms ± 7.68 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)`

---

<div class="post-metadata">

**Author:** ![Semi](https://avatars.discourse-cdn.com/v4/letter/s/a698b9/32.png) [@Semi](https://discourse.julialang.org/u/Semi)\
**Post date:** [April 10, 2021, 7:10pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/5 "2021-04-10T19:10:23Z")

</div>

Yes, I did that.

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [April 10, 2021, 7:16pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/6 "2021-04-10T19:16:43Z")

</div>

> [@Semi](#):
>
> ```julia
> def test(n,k,tmp2):
> for i in range(k):
> t = rand(n, n) 
> for j in range(k):
> tmp1 = rand(n, n)
> tmp2[i*n:(i+1)*n,j*n:(j+1)*n] = t*tmp1
> 
> ```

Your python code doesn’t run for me. Can you add the necessary imports and prefixes so I can check from copying and pasting?

---

<div class="post-metadata">

**Author:** ![raphbacher](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raphbacher/32/6019_2.png) [@raphbacher](https://discourse.julialang.org/u/raphbacher)\
**Post date:** [April 10, 2021, 7:18pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/7 "2021-04-10T19:18:03Z")

</div>

Hi, try switching the order of the loops i and j (python is row major vs Julia is column major).

---

<div class="post-metadata">

**Author:** ![Semi](https://avatars.discourse-cdn.com/v4/letter/s/a698b9/32.png) [@Semi](https://discourse.julialang.org/u/Semi)\
**Post date:** [April 10, 2021, 7:28pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/8 "2021-04-10T19:28:42Z")

</div>

Sure,

```julia
import numpy
from numpy import zeros
from numpy.random import rand

```

---

<div class="post-metadata">

**Author:** ![Semi](https://avatars.discourse-cdn.com/v4/letter/s/a698b9/32.png) [@Semi](https://discourse.julialang.org/u/Semi)\
**Post date:** [April 10, 2021, 7:29pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/9 "2021-04-10T19:29:45Z")

</div>

This did not make any difference.

---

<div class="post-metadata">

**Author:** ![Semi](https://avatars.discourse-cdn.com/v4/letter/s/a698b9/32.png) [@Semi](https://discourse.julialang.org/u/Semi)\
**Post date:** [April 10, 2021, 7:31pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/10 "2021-04-10T19:31:15Z")

</div>

And please change t\*tmp1 to t@tmp1, so that the comparison makes sense.

---

<div class="post-metadata">

**Author:** ![tbeason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tbeason/32/15898_2.png) [@tbeason](https://discourse.julialang.org/u/tbeason)\
**Post date:** [April 10, 2021, 7:38pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/11 "2021-04-10T19:38:49Z")

</div>

Stepping back a bit, I don’t think the thing that is slow here is “matrix operations”. You are creating lots and lots of somewhat large random matrices, as evidenced by the allocation numbers. If you just had 2 temporary arrays that you reused, you would see big speedups.

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [April 10, 2021, 7:43pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/12 "2021-04-10T19:43:35Z")

</div>

Thanks!

Interestingly, I don’t see the performance gain you mention.

```julia

In [8]: %timeit test(n,k,tmp2)
1.26 s ± 39.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

```

vs.

```julia
julia> @btime test($n,$k,$tmp2)
  1.178 s (3660 allocations: 1.23 GiB)

```

---

<div class="post-metadata">

**Author:** ![Semi](https://avatars.discourse-cdn.com/v4/letter/s/a698b9/32.png) [@Semi](https://discourse.julialang.org/u/Semi)\
**Post date:** [April 10, 2021, 7:45pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/13 "2021-04-10T19:45:06Z")

</div>

It seems this is not the case.  
For example

```julia
function test(n)
    k = 100
    t = rand(n, n)
    tmp1 = rand(n, n) 
    for i in 1:k
        tmp1 = t + t;
    end
    for j in 1:k
        tmp1 = tmp1*t;
    end
end

```

gives me

```julia
@btime test(100)
8.380 ms (404 allocations: 15.43 MiB)

```

while

```julia
import numpy
from numpy.random import rand
def test(n):
    k = 100
    t = rand(n, n)
    tmp1 = rand(n, n) 
    for i in range(k):
        tmp1 = t + t;
    for j in range(k):
        tmp1 = tmp1@t;

```

gives me

```julia
%timeit test(100)
2.38 ms ± 40.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

```

---

<div class="post-metadata">

**Author:** ![Semi](https://avatars.discourse-cdn.com/v4/letter/s/a698b9/32.png) [@Semi](https://discourse.julialang.org/u/Semi)\
**Post date:** [April 10, 2021, 7:48pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/14 "2021-04-10T19:48:33Z")

</div>

That’s very interesting!  
What are your specs?  
My colleague also tried this on a Windows machine and he obtained similar results as I did. I don’t know which version of Julia he used.

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [April 10, 2021, 7:51pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/15 "2021-04-10T19:51:38Z")

</div>

Here:

```julia
julia> versioninfo()
Julia Version 1.6.0
Commit f9720dc2eb (2021-03-24 12:55 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-11.0.1 (ORCJIT, skylake)
Environment:
  JULIA_PKG_DEVDIR = /home/peterwd/Documents/Development
  JULIA_EDITOR = subl

```

---

<div class="post-metadata">

**Author:** ![oheil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oheil/32/220745_2.png) [@oheil](https://discourse.julialang.org/u/oheil)\
**Post date:** [April 10, 2021, 7:59pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/16 "2021-04-10T19:59:03Z")

</div>

Can you check:

```julia
using LinearAlgebra
function test(n::Int64,k::Int64,tmp2::Array{Float64,2})
	buf=zeros(n,n)
    for i in 1:k
        t = rand(n, n) 
        for j in 1:k
            tmp1 = rand(n, n)
            tmp2[(i-1)*n+1:i*n,(j-1)*n+1:j*n] = mul!(buf, t, tmp1)
        end
    end
end
...

```

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [April 10, 2021, 8:04pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/17 "2021-04-10T20:04:19Z")

</div>

Julia has a different memory layout than Python, you’re doing the for-loop in the unfavourable order for Julia: [Performance Tips · The Julia Language](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-column-major)

---

<div class="post-metadata">

**Author:** ![oheil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oheil/32/220745_2.png) [@oheil](https://discourse.julialang.org/u/oheil)\
**Post date:** [April 10, 2021, 8:19pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/18 "2021-04-10T20:19:13Z")

</div>

This gives for me:

```julia
julia> @btime test($n,$k,$tmp2)
  866.969 ms (1862 allocations: 639.34 MiB)

```

But of course we have different systems and I refuse to install python now 😉

---

<div class="post-metadata">

**Author:** ![oheil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oheil/32/220745_2.png) [@oheil](https://discourse.julialang.org/u/oheil)\
**Post date:** [April 10, 2021, 8:23pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/19 "2021-04-10T20:23:18Z")

</div>

Switching off index checking gives a few ms more:

```julia
function test(n::Int64,k::Int64,tmp2::Array{Float64,2})
	buf=zeros(n,n)
    @inbounds for i in 1:k
        t = rand(n, n) 
        for j in 1:k
            tmp1 = rand(n, n)
			tmp2[(i-1)*n+1:i*n,(j-1)*n+1:j*n] = mul!(buf, t, tmp1)
        end
    end
end

julia> @btime test($n,$k,$tmp2)
  809.509 ms (1862 allocations: 639.34 MiB)

```

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [April 10, 2021, 8:24pm UTC](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004/20 "2021-04-10T20:24:00Z")

</div>

It’s very easy to figure out ways to speed up this function in Julia. But it remains an interesting, and unanswered, question why python is faster with “vanilla” code.

[Next page](https://discourse.julialang.org/t/why-is-this-simple-function-twice-as-slow-as-its-python-version/59004.md?page=2)
