# Julia not 220 times faster than C++

**URL:** <https://discourse.julialang.org/t/julia-not-220-times-faster-than-c/80512>\
**Category:** General Usage\
**Tags:** question, performance\
**Created:** [May 4, 2022, 9:38pm UTC](https://discourse.julialang.org/t/julia-not-220-times-faster-than-c/80512 "2022-05-04T21:38:04Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [May 4, 2022, 9:38pm UTC](https://discourse.julialang.org/t/julia-not-220-times-faster-than-c/80512/1 "2022-05-04T21:38:04Z")

</div>

I have a small piece of code that I actually use in my simulation, and just compared the performance between C++ and Julia. One function call needs about 7ns with Julia and 1550 ns with C++ and Eigen3.  
Why is C++ so slow? Am I doing something wrong?

```julia
using StaticArrays, Parameters, BenchmarkTools, LinearAlgebra

@with_kw mutable struct KPS{T}
    v_wind_tether::T = zeros(3)
    v_apparent::T = zeros(3)
    last_tether_drag::T = zeros(3)  
end

const kps = KPS{MVector{3, Float64}}()

function calc_drag!(s::KPS, v_segment, unit_vector, rho, v_app_perp, area)
    s.v_apparent .= s.v_wind_tether - v_segment
    v_app_norm = norm(s.v_apparent)
    v_app_perp .= s.v_apparent .- s.v_apparent ⋅ unit_vector .* unit_vector
    s.last_tether_drag .= -0.5 * rho * norm(v_app_perp) * area .* v_app_perp
    v_app_norm
end

@benchmark calc_drag!(kps, v_segment, unit_vector, rho, v_app_perp, area) setup=(
     v_segment=MVector(0.0, 0, 0); unit_vector=MVector(0.0,1,0); rho=1.0; 
     v_app_perp=MVector(10.0,0,0); area=0.3)

```

And the C++ version:

```julia
#include <iostream>
#include <Eigen/Dense>
#include <chrono>

using namespace std;
using Eigen::Vector3d;
using namespace std::chrono;

struct KPS {
    Vector3d v_wind_tether;
    Vector3d v_apparent;
    Vector3d last_tether_drag;
} ;

KPS kps;

double calc_drag(KPS s, Vector3d v_segment, Vector3d unit_vector, double rho, Vector3d v_app_perp, double area)
{
    s.v_apparent = s.v_wind_tether - v_segment;
    double v_app_norm = s.v_apparent.norm();
    v_app_perp = s.v_apparent - s.v_apparent.dot(unit_vector) * unit_vector;
    s.last_tether_drag = -0.5 * rho * v_app_perp.norm() * area * v_app_perp;
    return v_app_norm;
}

int main()
{
    // std::cout << kps.v_wind_tether << std::endl;
    Vector3d v_segment(0,0,0); Vector3d unit_vector(0,1,0); double rho = 1.0;
    Vector3d v_app_perp(10,0,0); double area = 0.3;
    auto start = high_resolution_clock::now();
    for(int i=0; i < 1000; i++) {
        calc_drag(kps, v_segment, unit_vector, rho, v_app_perp, area);
    }
    auto stop = high_resolution_clock::now();
    cout << "calc_drag needs "
         << ((stop - start).count())/1000
         << " ns." << endl;
}

```

And the file CMakeLists.txt:

```nohighlight
cmake_minimum_required(VERSION 3.0)

project(Tutorial)

find_package (Eigen3 3.3 REQUIRED NO_MODULE)

add_executable(${PROJECT_NAME} "main.cpp")
target_link_libraries (${PROJECT_NAME} Eigen3::Eigen)

```

My problem is, if I tell this on a scientific conference people won’t believe me.

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [May 4, 2022, 10:20pm UTC](https://discourse.julialang.org/t/julia-not-220-times-faster-than-c/80512/2 "2022-05-04T22:20:17Z")

</div>

I have never used `CMakeLists.txt` for long (started disliking it very soon), do it guarantees that the C++ code is being compiled with `-O3`?

---

<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:** [May 4, 2022, 10:39pm UTC](https://discourse.julialang.org/t/julia-not-220-times-faster-than-c/80512/3 "2022-05-04T22:39:50Z")

</div>

I think that you could improve the C++ code by passing const ref vec3d Eigen vectors.

If I remember well you can also avoid temp vector (vec\_apparent) by applying norm method to the difference expression. Something like (v1-v2).norm()

---

<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:** [May 5, 2022, 12:06am UTC](https://discourse.julialang.org/t/julia-not-220-times-faster-than-c/80512/4 "2022-05-05T00:06:57Z")

</div>

> [@Henrique\_Becker](#):
>
> do it guarantees that the C++ code is being compiled with `-O3` ?

You have to configure with [`-DCMAKE_BUILD_TYPE=Release`](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html), which I believe is _ **not** _ the default.

---

<div class="post-metadata">

**Author:** ![paulmelis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/paulmelis/32/35063_2.png) [@paulmelis](https://discourse.julialang.org/u/paulmelis)\
**Post date:** [May 5, 2022, 7:05am UTC](https://discourse.julialang.org/t/julia-not-220-times-faster-than-c/80512/5 "2022-05-05T07:05:53Z")

</div>

Some observations:

- The Julia and C++ versions of `calc_drag()` are not equivalent. The Julia version receives `s`, `v_segment` and friends _by reference_, while the C++ version receives them _by value_. Hence, the modifications within `calc_drag()` for the C++ version have no effect and it merely computes and returns `s.v_apparent.norm()`.

- Since the code is a single source file without any libraries to link (Eigen is a header-only library) you don’t really need CMake to build it. If you know the location of the Eigen headers it’s a single line of calling g++ to compile it to optimized code: `g++ -o t2 -O3 -march=native -I/usr/include/eigen3 t2.cc`.

- The C++ time you report seems to indicate it was compiled without optimization, which is the default for GCC if you don’t pass any `-O` flags (as CMake won’t do by default I believe). On my PC here (Core™ i5-4460 CPU @ 3.20GHz, GCC 11.2.0) turning on optimization makes the time per step go to zero:

- From what I read [here](https://en.cppreference.com/w/cpp/chrono/high_resolution_clock) `std::chrono::high_resolution_clock` isn’t the best clock source for these kind of measurements, as it is system-dependent which source is used and `std::chrono::steady_clock` is more appropriate. I have no experience with these classes, but assume that that info is correct.

All in all, if I update your original code to pass the parameters by reference and switch to the other clock class (see full code below) I get these timings:

```julia
melis@juggle 09:02:~$ ./t3
calc_drag needs 6.27421 ns.
melis@juggle 09:02:~$ ./t3
calc_drag needs 4.50141 ns.
melis@juggle 09:02:~$ ./t3
calc_drag needs 6.71724 ns.
melis@juggle 09:02:~$ ./t3
calc_drag needs 6.04979 ns.
melis@juggle 09:03:~$ ./t3
calc_drag needs 5.79616 ns.
melis@juggle 09:03:~$ ./t3
calc_drag needs 5.04113 ns.
melis@juggle 09:03:~$ ./t3
calc_drag needs 6.7191 ns.

```

So quite a bit of variance, but these at least appear to be in the same ballpark as what you report for the Julia version, which is what I would expect for such simple code.

```julia
#include <iostream>
#include <Eigen/Dense>
#include <chrono>

using namespace std;
using Eigen::Vector3d;
using namespace std::chrono;

struct KPS {
    Vector3d v_wind_tether;
    Vector3d v_apparent;
    Vector3d last_tether_drag;
} ;

KPS kps;

double calc_drag(KPS& s, Vector3d& v_segment, Vector3d& unit_vector, double rho, Vector3d& v_app_perp, double area)
{
    s.v_apparent = s.v_wind_tether - v_segment;
    double v_app_norm = s.v_apparent.norm();
    v_app_perp = s.v_apparent - s.v_apparent.dot(unit_vector) * unit_vector;
    s.last_tether_drag = -0.5 * rho * v_app_perp.norm() * area * v_app_perp;
    return v_app_norm;
}

int main()
{
    // std::cout << kps.v_wind_tether << std::endl;
    Vector3d v_segment(0,0,0); Vector3d unit_vector(0,1,0); double rho = 1.0;
    Vector3d v_app_perp(10,0,0); double area = 0.3;
    auto start = steady_clock::now();
    const int N = 100000;
    double res;
    for(int i=0; i < N; i++) {
        res += calc_drag(kps, v_segment, unit_vector, rho, v_app_perp, area);
    }
    auto stop = steady_clock::now();
    auto duration = stop - start;
    cout << "calc_drag needs "
         << (1.0f * duration_cast<nanoseconds>(duration).count() / N)
         << " ns." << endl;
}

```

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [May 5, 2022, 7:29am UTC](https://discourse.julialang.org/t/julia-not-220-times-faster-than-c/80512/6 "2022-05-05T07:29:00Z")

</div>

Thank you very much!

But should’t it be

```julia
kps&,

```

instead of

> [@paulmelis](#):
>
> `KPS& s,`

?

---

<div class="post-metadata">

**Author:** ![paulmelis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/paulmelis/32/35063_2.png) [@paulmelis](https://discourse.julialang.org/u/paulmelis)\
**Post date:** [May 5, 2022, 7:31am UTC](https://discourse.julialang.org/t/julia-not-220-times-faster-than-c/80512/7 "2022-05-05T07:31:02Z")

</div>

> [@ufechner7](#):
>
> But should’t it be
> 
> ```julia
> kps &,
> 
> ```
> 
> instead of
> 
> > [@paulmelis](#):
> >
> > `KPS& s,`
> 
> ?

Uh, no? The C++ syntax for passing by reference is `(<type>& <variable>, ...)`, e.g. `KPS& s`. Or do you mean something else?

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [May 5, 2022, 7:33am UTC](https://discourse.julialang.org/t/julia-not-220-times-faster-than-c/80512/8 "2022-05-05T07:33:13Z")

</div>

OK, thank you for freshening up my rusted C++ knowledge!  
Was programming too much in Julia in the last time…

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [May 5, 2022, 4:00pm UTC](https://discourse.julialang.org/t/julia-not-220-times-faster-than-c/80512/9 "2022-05-05T16:00:33Z")

</div>

My latest CPP code:

```cpp
#include <iostream>
#include <Eigen/Dense>
#include <chrono>

using namespace std;
using Eigen::Vector3d;
using namespace std::chrono;

struct KPS {
    Vector3d v_wind_tether;
    Vector3d v_apparent;
    Vector3d last_tether_drag;
} ;

KPS kps;

double calc_drag(KPS& s, Vector3d& v_segment, Vector3d& unit_vector, double rho, Vector3d& v_app_perp, double area)
{
    s.v_apparent = s.v_wind_tether - v_segment;
    double v_app_norm = s.v_apparent.norm();
    v_app_perp = s.v_apparent - s.v_apparent.dot(unit_vector) * unit_vector;
    s.last_tether_drag = -0.5 * rho * v_app_perp.norm() * area * v_app_perp;
    return v_app_norm;
}

int main()
{
    // std::cout << kps.v_wind_tether << std::endl;
    Vector3d v_segment(0.1,0.2,0.3); Vector3d unit_vector(0.0,1,0); double rho = 1.0;
    Vector3d v_app_perp(10,0,0); double area = 0.3;
    double v_app_norm;
    auto start = high_resolution_clock::now();
    for(int i=0; i < 100000; i++) {
       v_app_norm = calc_drag(kps, v_segment, unit_vector, rho, v_app_perp, area);
    }
    auto stop = high_resolution_clock::now();
    cout << "calc_drag needs "
         << ((stop - start).count())/100000
         << " ns." << endl;
}

```

And my CMakeLists.txt:

```julia
cmake_minimum_required(VERSION 3.0)

project(Tutorial)

find_package (Eigen3 3.3 REQUIRED NO_MODULE)

add_executable(${PROJECT_NAME} "main.cpp")
target_link_libraries (${PROJECT_NAME} Eigen3::Eigen)

if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE Release)
endif()

set(CMAKE_CXX_FLAGS "-Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O2")

```

Which gives an execution time of 5ns with -O2 optimization level, the same level I used for Julia.

But measuring these very short times is tricky on C++.

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [May 5, 2022, 4:03pm UTC](https://discourse.julialang.org/t/julia-not-220-times-faster-than-c/80512/10 "2022-05-05T16:03:10Z")

</div>

But what I do not understand: Why is :

```julia
   s.last_tether_drag = -0.5 * rho * v_app_perp.norm() * area * v_app_perp;

```

not allocating any memory? In Julia we need .= to achieve an non-allocating assignment. Is this not needed in C++ ?

---

<div class="post-metadata">

**Author:** ![WschW](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wschw/32/6575_2.png) [@WschW](https://discourse.julialang.org/u/WschW)\
**Post date:** [May 5, 2022, 4:11pm UTC](https://discourse.julialang.org/t/julia-not-220-times-faster-than-c/80512/11 "2022-05-05T16:11:22Z")

</div>

C++ supports overloading `=` and Eigen takes advantage of that to avoid allocations.
