# Performance comparison with C++

**URL:** <https://discourse.julialang.org/t/performance-comparison-with-c/29682>\
**Category:** Performance\
**Created:** [October 9, 2019, 1:33pm UTC](https://discourse.julialang.org/t/performance-comparison-with-c/29682 "2019-10-09T13:33:00Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)\
**Post date:** [October 9, 2019, 1:33pm UTC](https://discourse.julialang.org/t/performance-comparison-with-c/29682/1 "2019-10-09T13:33:00Z")

</div>

I found a very simple task where the Julia performances are unexpectedly and significantly worse than C++.

The core function to be benchmarked is as follows:

```julia
function subr!(d1, nx, ny, d2)
    for iy in 1:ny
        for ix in 1:nx
            d2[ix,iy] = d1[ix,iy] + ix + iy
        end
    end
end

```

where both `d1` and `d2` are `Matrix{Float32}`. The complete MWE for both Julia and C++ are available at the bottom of this post.

The performance ratio with respect to C++ (`-O3`) is of the order of ~5. The original author of the benchmark (see below) found an even worse ratio of ~10.

My julia version is:

```julia
julia> versioninfo()
Julia Version 1.1.0
Commit 80516ca202 (2019-01-21 21:24 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.1 (ORCJIT, skylake)

```

but I obtained the same results with v1.3.0-rc2.

I tried all the tricks I am aware of. Among these:

- start julia with `julia -O3`;
- inspect the output of `@code_warntype`;
- exchange row/column access order;
- using `@simd`, `@inbounds`, `@fastmath` in many combinations;
- using a `Vector{Vector{Float32}}` (as it actually works in C++), rather than a `Matrix{Float32}`;

but none solved the problem.

Is there a reason why the Julia performances are so bad in this case?  
Is there a way to improve performances by slightly tweaking the code shown below?

More info (as well as a comparison with many other languages) is available in the repo of the original author of the [benchmark](https://github.com/KnaveAndVarlet/ADASS2019).

MWE in C++:

```nohighlight
// Compile with:
// g++ -o test -O3 test.cpp

#include <stdio.h>
#include <stdlib.h>

void subr (float* d1[], int nx, int ny, float* d2[]) {
	for (int iy = 0; iy < ny; iy++)
		for (int ix = 0; ix < nx; ix++)
			d2[iy][ix] = d1[iy][ix] + ix + iy;
}

int main (int argc, char* argv[]) {
	int nrpt = 1000000;
	int nx = 2000;
	int ny = 10;
	printf ("Arrays have %d rows of %d columns, repeats = %d\n",ny,nx,nrpt);

	// Allocate and initialize
	float* d1Data = (float*) malloc (nx * ny * sizeof(float));
	float* d2Data = (float*) malloc (nx * ny * sizeof(float));
	float**d1 = (float**) malloc (ny * sizeof(float*));
	float**d2 = (float**) malloc (ny * sizeof(float*));
	for (int iy = 0; iy < ny; iy++) {
		d1[iy] = d1Data + iy * nx;
		d2[iy] = d2Data + iy * nx;
	}
	for (int iy = 0; iy < ny; iy++)
		for (int ix = 0; ix < nx; ix++)
			d1[iy][ix] = float(nx - ix + ny - iy);

	// Main job
	for (int Loop = 0; Loop < nrpt; Loop++) {
		subr(d1, nx, ny, d2);
	}

	// Check the results (all cells should contain the same value: nx+ny).
	bool Error = false;
	for (int iy = 0; iy < ny; iy++) {
		for (int ix = 0; ix < nx; ix++) {
			if (d2[iy][ix] != nx+ny) {
				Error = true;
				printf ("Error Out[%d][%d] = %f, not %f\n",iy,ix,
						d2[iy][ix],float(d1[iy][ix] + ix + iy));
				break;
			}
		}
		if (Error) break;
	}
	return 0;
}

```

MWE in Julia:

```julia
function subr!(d1, nx, ny, d2)
    for iy in 1:ny
        for ix in 1:nx
            d2[ix,iy] = d1[ix,iy] + ix + iy
        end
    end
end

function main()
    nrpt = 1000000
    nx = 2000
    ny = 10
    println("Arrays have ",ny," rows of ",nx," columns, repeats = ",nrpt)

    # Allocate and initialize
    d1 = zeros(Float32,nx,ny)
    d2 = zeros(Float32,nx,ny)
    for iy in 1:ny, ix in 1:nx
        d1[ix,iy] = nx - ix + ny - iy
    end

    # Warm-up
    subr!(d1,nx,ny,d2)
    
    # Main job
    @time for irpt in 1:nrpt
        subr!(d1, nx, ny, d2)
    end

    # Check the results (all cells should contain the same value: nx+ny).
    for iy in 1:ny, ix in 1:nx
        if d2[ix,iy] != nx + ny
            println("error ", d2[ix,iy]," ",ix," ",iy)
            break
        end
    end
end

main()

```

---

<div class="post-metadata">

**Author:** ![Maurizio\_Tomasi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/maurizio_tomasi/32/384_2.png) [@Maurizio\_Tomasi](https://discourse.julialang.org/u/Maurizio_Tomasi)\
**Post date:** [October 9, 2019, 1:41pm UTC](https://discourse.julialang.org/t/performance-comparison-with-c/29682/2 "2019-10-09T13:41:42Z")

</div>

Hi @gcalderone, are you attending ADASS too?

I too download the code and tried to optimize it, but with no avail: I basically tried the same tricks as you (`@simd`, `@inbounds`, even wrapping all the loops in functions), but timing never changed.

---

<div class="post-metadata">

**Author:** ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)\
**Post date:** [October 9, 2019, 1:43pm UTC](https://discourse.julialang.org/t/performance-comparison-with-c/29682/3 "2019-10-09T13:43:26Z")

</div>

Unfortunately not… But I friend of mine sent me the link to the repo 😉

Still, I was quite surprised at the result. I hope someone will provide some solution…

---

<div class="post-metadata">

**Author:** ![jackf](https://avatars.discourse-cdn.com/v4/letter/j/b4bc9f/32.png) [@jackf](https://discourse.julialang.org/u/jackf)\
**Post date:** [October 9, 2019, 1:55pm UTC](https://discourse.julialang.org/t/performance-comparison-with-c/29682/4 "2019-10-09T13:55:43Z")

</div>

Is it because the julia matrix is column major, and you’re accessing it row by row? At first glance it looks like your iteration order is the same in C++ and julia

EDIT: no, probably isn’t this - hadn’t noticed you are swapping the indices when accessing the matrix

---

<div class="post-metadata">

**Author:** ![saschatimme](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/saschatimme/32/10313_2.png) [@saschatimme](https://discourse.julialang.org/u/saschatimme)\
**Post date:** [October 9, 2019, 2:15pm UTC](https://discourse.julialang.org/t/performance-comparison-with-c/29682/5 "2019-10-09T14:15:04Z")

</div>

The problem is you are not comparing the same code. In your C++ code the `subr!` routine uses `Int32` indices in the for loop and in your Julia code you have `Int64` indices. This is usually not a problem, but you add these to `Float32` number. IIRC there is a SIMD instruction to convert Int32 to float but not for Int64 (resp only with AVX 512).

By using

```julia
function subr!(d1, nx, ny, d2)
    @inbounds for iy in Int32(1):Int32(ny), ix in Int32(1):Int32(nx)
        d2[ix,iy] = d1[ix,iy] + ix + iy
    end
end

```

I get the desired 5x speedup.

---

<div class="post-metadata">

**Author:** ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)\
**Post date:** [October 9, 2019, 2:16pm UTC](https://discourse.julialang.org/t/performance-comparison-with-c/29682/6 "2019-10-09T14:16:12Z")

</div>

```julia
function subr!(d1, nx, ny, d2)
    for iy in Int32(1):ny
        for ix in Int32(1):nx
            @inbounds d2[ix,iy] = d1[ix,iy] + ix + iy
        end
    end
end

```

and declaring the other ints passed in as Int32 makes it vectorize and get the speedup ([Intel® Intrinsics Guide](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=vcvtdq2ps&expand=1452), [CVTDQ2PS — Convert Packed Doubleword Integers to Packed Single-Precision Floating-Point Values](https://www.felixcloutier.com/x86/cvtdq2ps)).

Edit: Nvm, sniped!

---

<div class="post-metadata">

**Author:** ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)\
**Post date:** [October 9, 2019, 2:23pm UTC](https://discourse.julialang.org/t/performance-comparison-with-c/29682/7 "2019-10-09T14:23:41Z")

</div>

Great, I knew there must have been a simple solution.  
Thanks to @saschatimme and @kristoffer.carlsson!

I [notified](https://github.com/KnaveAndVarlet/ADASS2019/issues/1) the author of the benchmark.

---

<div class="post-metadata">

**Author:** ![sairus7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sairus7/32/10816_2.png) [@sairus7](https://discourse.julialang.org/u/sairus7)\
**Post date:** [October 10, 2019, 8:33am UTC](https://discourse.julialang.org/t/performance-comparison-with-c/29682/8 "2019-10-10T08:33:49Z")

</div>

So, if I need speed-up in critical for loops with `Float` arrays, I should explicitly use `Int32` instead of built-in `Int64` type? And add that conversion to every index variable initialization?

---

<div class="post-metadata">

**Author:** ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)\
**Post date:** [October 10, 2019, 8:39am UTC](https://discourse.julialang.org/t/performance-comparison-with-c/29682/9 "2019-10-10T08:39:40Z")

</div>

It depends:

- if you use integer just for indexing the answer is **no** ;
- if you use the indexing integers in a math expression involving floating point numbers, then **maybe** ;
