# Do C++ performance optimisations apply to Julia?

**URL:** <https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567>\
**Category:** Performance\
**Created:** [June 10, 2022, 2:19pm UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567 "2022-06-10T14:19:35Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![jacobusmmsmit](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jacobusmmsmit/32/217669_2.png) [@jacobusmmsmit](https://discourse.julialang.org/u/jacobusmmsmit)\
**Post date:** [June 10, 2022, 2:19pm UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/1 "2022-06-10T14:19:35Z")

</div>

Hello everyone 🙂

I came across a set of tips for optimising C++ raytracing code written by [Chris Wyman](https://cwyman.org/) and wondered how much of it applies to Julia. In total there are 27 tips which I’ll share below along with my thoughts on them. What I would like to gain from this post is to ask to fill in the numerous gaps in my knowledge about many of the optimisations as they relate to lower level or core components of the Julia language or computer architecture in general.

My ultimate goal is to make a blog/Forem post about some more subtle, uncommon, or counterintuitive (coming from other languages) optimisations that one can make to Julia code. The bonus goal of this post is to compare how these optimisations apply to Python. A lot of newer programmers and college students (myself included) _love_ to make code go fast and I think it would be fun way to introduce them to Julia: favourably comparing something they are familiar with, namely Python and/or C++, to something that most wouldn’t have considered otherwise.

The original pdf can be found [here](https://people.cs.clemson.edu/~dhouse/courses/405/papers/optimize.pdf) but I will summarise (sometimes copying sentences directly, obviously I wouldn’t do that for the article) the points without further ado:

Some general rules that apply to all languages:

1. _Remember Ahmdal’s Law_: \text{speedup} := \frac{\text{time}\_\text{old}}{\text{time}\_\text{new}} = \frac{1}{(1-\text{func}\_\text{cost}) + \text{func}\_\text{cost}/\text{func}\_\text{speedup}}
  - Clearly this still applies in Julia as it’s just a good thing to keep in mind before foolishly trying to optimise a piece of code that is rarely called.
  - “Make the common case fast and the rare case correct”.

2. _Code for correctness first, then optimize!_
  - Write for correctness, then if you know the function will be called frequently perform obvious optimisations then find and remove bottlenecks. (paraphrasing the original)

3. _People I know who write very efficient code say they spend at least twice as long optimizing code as they spend writing code._

Some more specific tips:

1. _Jumps/branches are expensive. Minimize their use whenever possible._

2. _Think about the order of array indices._

3. _Think about instruction-level-parallelism._

4. _Avoid/reduce the number of local variables._

5. _Reduce the number of function parameters_

(From here on out my knowledge gets really spotty as I’ve never really learned about memory or used C/C++ etc.)

1. _Pass structures by reference, not by value_

2. _If you do not need a return value from a function, do not define one_

3. _Try to avoid casting where possible_

4. _Be very careful when declaring C++ object variables._

5. _Make default class constructors as lightweight as possible_

6. _Use shift operations instead of integer multiplication and division, where possible._

7. _Be careful using table-lookup functions_

8. _For most classes, use the operators `+=` , `-=` , `*=` , and `/=` , instead of the operators `+` , `-` , `*` , and `/` ._

9. _For basic data types, use the operators `+` , `-` , `*` , and `/` instead of the operators `+=` , `-=` , `*=` , and `/=` ._

10. _Delay declaring local variables_

11. _For objects, use the prefix operator (++obj) instead of the postfix operator (obj++)._

12. _Be careful using templates_

13. _Avoid dynamic memory allocation during computation._

14. _Find and utilize information about your system’s memory cache_

15. _Avoid unnecessary data initialization._

16. _Try to early loop termination and early function returns._

17. _Simplify your equations on paper!_

18. _The difference between math on integers, fixed points, 32-bit floats, and 64-bit doubles is not as big as you might think._

19. _Consider ways of rephrasing your math to eliminate expensive operations._

And that’s everything. I wish I had more to say about many of these, but I hope that with the community’s advice I can write something insightful about most of these comments, and hopefully give people looking to scratch their optimisation itch while using a high-level language more of a reason to use Julia!

As I’m sure you can tell, my knowledge of the inner workings of Julia, C++, and the very computers that I use every day are limited so I’m definitely not the best, nor most qualified person to write this article. That said, the more content there is about my favourite language the better!

---

<div class="post-metadata">

**Author:** ![gbaraldi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gbaraldi/32/22101_2.png) [@gbaraldi](https://discourse.julialang.org/u/gbaraldi)\
**Post date:** [June 10, 2022, 2:41pm UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/2 "2022-06-10T14:41:25Z")

</div>

In summary, most of those apply, julia doesn’t generate code that much different from C++ when you compare assembly. Specifically about inlining, julia is very agressive when it comes to inlining, so you generally don’t need to worry about it. There are some edge cases where julia get’s it wrong but they are just that.

The thing about local variables is odd, I would avoid doing that even in C++ since global variables make code harder regardless of performance.

As you said smaller floats and integers can make massive differences due to being less memory and better SIMD.

Julia structs are basically like C/C++ structs, and when parametric they are similar to templates.

Initializing memory is not allocating memory, we preallocate, but sometimes allocating with `undef` is faster than initializing it with something like `zeros`.

In relation to templates, julia code uses generics for everything and emits specialized code if it’s type stable so making sure your code is type stable is enough.

Some of the optimizations mentioned are routinely done by the compiler so I wouldn’t worry too much.

Edit: About python, the optimization is to call code that isn’t python 🙂 .

---

<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:** [June 10, 2022, 2:57pm UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/3 "2022-06-10T14:57:52Z")

</div>

> [@jacobusmmsmit](#):
>
> Another piece of advice is the use of switch statements to replace `if...elseif...elseif` chains as these also require jumps, whereas a switch can be optimised into a table lookup with a single jump.

I needs someone else to confirm this, but I believe that these chains can be optimised by the compiler to the equivalent of a switch statements if all conditionals compare the same variable to a single value each.

> [@jacobusmmsmit](#):
>
> 1. Avoid/reduce the number of local variables.

This is pretty nonsensical to Julia, I believe. In C++ each local variable is kinda guaranteed to be a real memory position (either in the stack or the registers), in Julia the variables are bindings, just names for values, that do not give the same guarantees (both for better and for worse).

> [@jacobusmmsmit](#):
>
> 1. Reduce the number of function parameters
> 
> - Paraphrasing: “Same reason as above, these are also stored on the stack.”

From what I have seen in this forum, in Julia we often are worried about putting things in the stack instead of the heap, not in the registers instead of the stack. This is basically going one extra level of optimization that I do not know if it can reliably be achieved by Julia.

> [@jacobusmmsmit](#):
>
> 1. Pass structures by reference, not by value

You do not have the same level of control over this in Julia than in other languages, and I do not really agree with this general statement. Very large structs may be better passed as reference, but (i) you will be paying the cost of de-reference and (ii) this is decided by Julia itself (mutable are always passed by reference, and immutable are left to the compiler to decide). You can wrap values with `Ref` but I think the extra indirection layer will almost always be detrimental to performance. I also believe this comment may be a little old, many compiler optimisations nowadays take advantage of the invariants that come from immutability.

> [@jacobusmmsmit](#):
>
> 1. If you do not need a return value from a function, do not define one

I am not sure if I did not understand the advice or your comment. It seems like the advice is about returning `void` (i.e., not having a return value) but I may be wrong. If it is about not defining functions per se it seems a bit nonsensical to me. You never really need the “return value from a function” you can always expand the function at your context using the variables that would be the arguments (if it is the call cost you are worried).

> [@jacobusmmsmit](#):
>
> 1. Be very careful when declaring C++ object variables.

I really think that in Julia, the rare `local i; i = 1` will be no different than `local i = 1`, the compiler is not braindead.

> [@jacobusmmsmit](#):
>
> 1. Use shift operations instead of integer multiplication and division, where possible

Again, I would be surprised if the highest level of compiler optimization did not do this by you.

> [@jacobusmmsmit](#):
>
> 1. Delay declaring local variables

Again, this seems like something the compiler should be able to deal with, and in Julia the `if` statements are leaky, so I do not think it applies (in C/C++ they aren’t if my memory do not fail me).

> [@jacobusmmsmit](#):
>
> 1. Avoid unnecessary data initialization.

I think the Julian equivalent is using `Vector{T}(undef, n)` instead of `fill(value, n)` when the initialization is unnecessary.

---

<div class="post-metadata">

**Author:** ![vchuravy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vchuravy/32/8_2.png) [@vchuravy](https://discourse.julialang.org/u/vchuravy)\
**Post date:** [June 10, 2022, 4:34pm UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/4 "2022-06-10T16:34:23Z")

</div>

> [@jacobusmmsmit](#):
>
> Is iteration still preferred (for performance) over recursion when you keep everything type stable?

This is very subtle. Recursion is easier for the inference algorithm to reason about, so sometimes you will see rather tricky recursion patterns used in code that in particular deals with tuples. But often iteration is better if you are dealing with homogenous types and you avoid that the user gives you a very large input and thus blow the stack-limit.

> [@jacobusmmsmit](#):
>
> The advice given encourages the use of global variables to avoid using too many local ones,

The closest corollary in Julia is the use of StaticArrays. Sometimes the question arises: What is to big for an SArray, SArray tend to live on the stack and so there is a usable limit here and it might be more efficient to use a sized array.

> [@jacobusmmsmit](#):
>
> Pass structures by reference, not by value

[Functions · The Julia Language](https://docs.julialang.org/en/v1/manual/functions/#Argument-Passing-Behavior) I think the thing to discuss here is `struct` vs `mutable struct`.

> [@jacobusmmsmit](#):
>
> _Be careful using templates_

The equivalent of templates is parametric types/type parameters in a function/and to a more extreme extend generated functions.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [June 10, 2022, 6:37pm UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/5 "2022-06-10T18:37:45Z")

</div>

> [@jacobusmmsmit](#):
>
> - _Avoid unnecessary data initialization._
> - “If you must initialize a large chunk of memory, consider using `memset()`.”
> - From what I know, `memset` is a fundamental part of C++ optimisation, but Julians don’t need to worry about it.
> - If anything, preallocating large arrays is one of the most Julian things to do and is something that even MATLAB users have in their optimisation toolkit.

You are conflating memory allocation and initialization. They are not the same.

In Julia, we have nice tools to track memory allocation. To optimize, one should avoid allocating memory as much as possible. Preallocation is mainly used to create buffers and to avoid frequent allocation such as in loops.

In Julia, `memset` is called `fill!`:

> <https://github.com/JuliaLang/julia/blob/ab4c8cf616f222ae39767a46957ba06c4b77a0f9/base/array.jl#L425-L428>

This call could be optimized away by the compiler. Because of this, we have `Base.securezero!`:

```julia
help?> Base.securezero!
  securezero!(o)

  securezero! fills the memory associated with an object o with zeros. Unlike fill!(o,0) and similar code, which might be optimized away by the compiler for objects about to be discarded, the
  securezero! function will always be called.

```

Like in C++, it preferable to not initialize if not needed. `Array{T}(undef, ...)` is preferred over `zeros(T, ...)`. If needed, `fill!` (aka memset) is a good way to do so. It may be optimized out, which in some cases may be a problem.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [June 10, 2022, 6:50pm UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/6 "2022-06-10T18:50:26Z")

</div>

At the same time, it is rarely the case that memory allocation or initialization of an allocatable array is a major part of a performance critical function. If it is, one should probably be preallocating outside the critical part, and thus initializing or not is probably irrelevant. Most times this is likely just a premature optimization of very minor importance.

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [June 10, 2022, 7:34pm UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/7 "2022-06-10T19:34:13Z")

</div>

I am dubious as to if avoiding memory on the stack does anything meaningful to optimize C++.  
Especially with modern optimizing compilers.

Ensuring memory ends up on the stack and not the heap is a good optimization in julia.  
Since you speed things up by removing the time taken to allocate, and deallocate and garbage collect (the former two also apply in C++, the last doesn’t).

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [June 10, 2022, 8:50pm UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/8 "2022-06-10T20:50:44Z")

</div>

> [@vchuravy](#):
>
> The closest corollary in Julia is the use of StaticArrays. Sometimes the question arises: What is to big for an SArray, SArray tend to live on the stack and so there is a usable limit here and it might be more efficient to use a sized array.

Because `SArray` are immutable and the conversion to and from mutable `MArray`s are expensive, and also because operations on `SArray` tend to be defined to be entirely unrolled, they scale very poorly.  
If it weren’t for these two factors, the usable size range of `SArray`s could be larger.  
Although, if enough people were to stack allocate 50x50 arrays, we’d likely start blowing the stack, so maybe it’s better we don’t encourage people to put big arrays on the stack.

> I am dubious as to if avoiding memory on the stack does anything meaningful to optimize C++.

I do think using stack memory helps in C++, which is why LLVM makes extensive use of data types like `llvm::SmallVector` internally. The `llvm::SmallVector` is a dynamically sized array that allocates some memory on the stack. If the size of the array is small enough to fit on this stack space, it is stack allocated. Otherwise, it’ll allocate on the heap.

It is very common for Julia program runtimes be dominated by allocations.  
It’s not uncommon to see \>50% GC time.  
If one intends to write fast Julia code, thinking about memory management is a critical part of the design.  
But I think that is true of any language where writing fast code is on the table.

This is a big part of the difference in performance between SimpleChains.jl and Flux.jl.

---

<div class="post-metadata">

**Author:** ![jacobusmmsmit](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jacobusmmsmit/32/217669_2.png) [@jacobusmmsmit](https://discourse.julialang.org/u/jacobusmmsmit)\
**Post date:** [June 10, 2022, 9:40pm UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/9 "2022-06-10T21:40:48Z")

</div>

Thank you so much everyone for the replies so far. Apologies that I posted and then didn’t engage at all! I’ll start collating ideas and write a first draft which you can all pick apart if you like : - )

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [June 11, 2022, 12:40am UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/10 "2022-06-11T00:40:05Z")

</div>

> [@Elrod](#):
>
> Because `SArray` are immutable and the conversion to and from mutable `MArray`s are expensive, and also because operations on `SArray` tend to be defined to be entirely unrolled, they scale very poorly.  
> If it weren’t for these two factors, the usable size range of `SArray`s could be larger.  
> Although, if enough people were to stack allocate 50x50 arrays, we’d likely start blowing the stack

So how much of this applies to immutable structs? Nobody would bother writing one with 2500 fields, but I am wondering how immutables are implemented. Probably off-topic, but my curiosity started over whether a compiler could edit an individual field in an immutable struct instance (which may also be an `Array` element rather than living on the stack) under the hood rather than the semantic action of creating a new instance. It seems like a useful optimization for `setfield!`-like syntax and operations if there’s no need for the change to be shared by multiple references (a job for mutable structs with all their downsides). I know about `Setfield.jl` but I have yet to figure out how to edit a field of an `Array` element without copying the whole array.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [June 11, 2022, 9:55am UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/11 "2022-06-11T09:55:33Z")

</div>

> [@jacobusmmsmit](#):
>
> Thank you so much everyone for the replies so far. Apologies that I posted and then didn’t engage at all! I’ll start collating ideas and write a first draft which you can all pick apart if you like : - )

Thank you for checking in with the community on this. My general sense is that the 27 points are likely more applicable to Julia than not. The main question is how important are they. If they are not applicable to Julia, I would point to a specific Julia feature rather than philosophical differences.

> [@jacobusmmsmit](#):
>
> - 
> - The advice given encourages the use of global variables to avoid using too many local ones, which appears to go directly goes against the first rule of Julian law. However, if we are already inside a locally isolated scope, is there reason to avoid allocating local variables in - for example - a function defined in this scope?

In Julia, the advice to avoid is global dynamically-typed variables. In some cases, global constant references may be a useful optimization. Constant references with concrete types are useful because they can be statically typed in that they can take new values but cannot change type. Another approach would be passing reusing an argument rather creating a new variable. Methods ending with `!` particularly emphasize this pattern.

> [@jacobusmmsmit](#):
>
> - 
> - Here’s where we definitely reach the end of my low-ish level knowledge. Pointers/`Ref`s definitely don’t work the same way in C++ and Julia so I’d appreciate as much help on this one as possible.

Note that structs in Julia are by default immutable. This is an important language feature.

Mutable structs are passed by reference in Julia.

> [@jacobusmmsmit](#):
>
> - _If you do not need a return value from a function, do not define one_
> - Definitely not the Julian way: functions are everywhere because of dispatch and many exist purely to mutate state of pre-existing variables.
> - If there’s any deeper truth to this that I’m missing or some additional context that would be useful please let me know!

Having type-stable stable functions is important in Julia. If the compiler can determine the return type from the argument types, then less dynamic dispatch is needed. Thus, this advice applies to Julia as well. If no return value is needed, consider returning `nothing`.

> [@jacobusmmsmit](#):
>
> - 
> - Certainly applies to mutable structs and structs that contain mutable objects, however sometimes things that live entirely on the stack can be optimised away and are never initialised in the first place. If someone has any more in depth or specific knowledge in this area I’d appreciate it!

Some operations can be optimized through fusion. In Julia also consider is the use of `.=` and operations such as `.*=`. For example, note the allocation when using `f!` as opposed to `g!`.

```julia
julia> f!(A) = A .= A*2
f! (generic function with 1 method)

julia> g!(A) = A .*= 2
g! (generic function with 1 method)

...

julia> @time f!(A);
  0.000018 seconds (1 allocation: 8.125 KiB)

julia> @time g!(A);
  0.000011 seconds

```

> [@jacobusmmsmit](#):
>
> Unfortunately, most Julia packages are written by academics with specific use-cases.

I always find this comment weird. Academics write a lot of code in many languages and often release it as open source. The overall prevalence of open source code written by academics is high. It really comes down to the individual experience of the each author.

> [@jacobusmmsmit](#):
>
> - 
> - This is really cool stuff but I’ve never thought about it in Julia. How lightweight are structs in Julia? That is to say, how much overhead is there when allocating a struct compared to allocating its contents individually? Can I reliably predict how many bytes a struct will be?

Julia structs are often directly mappable to C structs. We rely on this for interoperability with C via `@ccall`. See the function `sizeof`.

> [@jacobusmmsmit](#):
>
> _The difference between math on integers, fixed points, 32-bit floats, and 64-bit doubles is not as big as you might think._

We think about this a lot in Julia because numerical computing is a major application of the language. We also have a type promotion system. On a small scale, it probably does not matter. On a large scale, memory can be important due to processor memory caches.

> [@jacobusmmsmit](#):
>
> - 
> - What about SIMD? Even if the original operations aren’t that much faster, the fact that we can do more of them is surely a bonus. Given that Julia (and everything compiled by Clang) heavily relies on SIMD code the benefit should be noticeable or at least tested for.

If you use smaller types, you can potential fit more of them into a 256 or 512 bit SIMD register and thus do more operations per cycle. Not all of this is as automatic as one would like though, so sometimes using SIMD explicitly is helpful.

---

<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:** [June 11, 2022, 10:16am UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/12 "2022-06-11T10:16:30Z")

</div>

> [@mkitti](#):
>
> Mutable structs are passed by reference in Julia.

No, Julia (like many lisps, Java, Perl, Python, Ruby, and others) uses [passing by sharing](https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing). This is also [documented](https://docs.julialang.org/en/v1/manual/functions/#Argument-Passing-Behavior).

---

<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:** [June 11, 2022, 10:28am UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/13 "2022-06-11T10:28:14Z")

</div>

> [@giordano](#):
>
> No, Julia (like many lisps, Java, Perl, Python, Ruby, and others) uses [passing by sharing](https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing). This is also [documented](https://docs.julialang.org/en/v1/manual/functions/#Argument-Passing-Behavior).

How is that different from pass-by-reference?

> Function arguments themselves act as new variable _bindings_ (new locations that can refer to values), but the values they **refer** to are identical to the passed values.

---

<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:** [June 11, 2022, 10:35am UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/14 "2022-06-11T10:35:43Z")

</div>

Quoting from the linked Wikipedia article:

> The semantics of call by sharing differ from call by reference: “In particular it is not call by value because mutations of arguments performed by the called routine will be visible to the caller. And it is not call by reference because access is not given to the variables of the caller, but merely to certain objects”.[[36]](https://en.m.wikipedia.org/wiki/Evaluation_strategy#cite_note-FOOTNOTECLU_Reference_Manual197414-15-36) So, for example, if a variable was passed, it is not possible to simulate an assignment on that variable in the callee’s scope.[[37]](https://en.m.wikipedia.org/wiki/Evaluation_strategy#cite_note-37) However, since the function has access to the same object as the caller (no copy is made), mutations to those objects, if the objects are [mutable](https://en.m.wikipedia.org/wiki/Mutable_object), within the function are visible to the caller, which may appear to differ from call by value semantics. Mutations of a mutable object within the function are visible to the caller because the object is not copied or cloned—it is shared.

---

<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:** [June 11, 2022, 10:54am UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/15 "2022-06-11T10:54:26Z")

</div>

Okay, I see the subtle difference between pass-by-reference and pass-by-sharing, but I still think the original quote that mutable structs are passed by reference is true (and more insightful). In that specific case a reference to the struct is passed (and so the struct can be mutated in the caller), even though pass-by-sharing is more accurate when conceptually describing how values of all types are passed in Julia.

---

<div class="post-metadata">

**Author:** ![skleinbo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skleinbo/32/36080_2.png) [@skleinbo](https://discourse.julialang.org/u/skleinbo)\
**Post date:** [June 11, 2022, 11:10am UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/16 "2022-06-11T11:10:01Z")

</div>

> [@paulmelis](#):
>
> original quote that mutable structs are passed by reference is true

It’s not though. If it were, the following would return `[1,2,3,]`

```julia
function modx(x)
  x = [1,2,3]
end
function f()
  x = [0]
  modx(x)
   ## returns [0] because x is passed-by-sharing, not reference
  return x
end

```

Compare pass-by-reference in C++

```cpp
#include <iostream>
#include <string>
#include <vector>

void modx(std::vector<int>& x){
    x = {1,2,3};
}
void f() {
    std::vector<int> x = {0};
    modx(x);  
    for (int n: x) {
        std::cout<<n<<",";
    }
}

int main()
{
  f(); ## prints '1,2,3,'
}

```

While the former behavior is sane and passing-by-reference as the default will lead to disaster (IMHO), telling someone who comes from e.g. C that Julia is pass-by-reference, might mislead and confuse them.

---

<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:** [June 11, 2022, 11:28am UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/18 "2022-06-11T11:28:52Z")

</div>

> [@skleinbo](#):
>
> > [@paulmelis](#):
> >
> > original quote that mutable structs are passed by reference is true
> 
> It’s not though. If it were, the following would return `[1]`

Hmm, seems I overlooked the case of assigning to the reference. I was mostly thinking of this usage (in which I see no difference between pass-by-sharing and pass-by-reference):

```julia
julia> mutable struct S
           f::Float32
       end

julia> s = S(1.234)
S(1.234f0)

julia> function mods(s)
           s.f = 456
       end
mods (generic function with 1 method)

julia> s
S(1.234f0)

julia> mods(s)
456

julia> s
S(456.0f0)

```

But okay, I learned something today on pass-by-sharing 🙂

---

<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:** [June 11, 2022, 11:43am UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/19 "2022-06-11T11:43:48Z")

</div>

> [@skleinbo](#):
>
> It’s not though. If it were, the following would return `[1,2,3,]`

Note that assignment never modifies the memory object pointed to by a variable but rebinds the variable to something else. Your `modx` doesn’t really modify in-place anything. If for example you `push!` to `x` you’re actually modifying it and the caller would see it.

---

<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:** [June 11, 2022, 11:50am UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/20 "2022-06-11T11:50:10Z")

</div>

> [@giordano](#):
>
> Note that assignment never modifies the memory object pointed to by a variable but rebinds the variable to something else. Your `modx` doesn’t really modify in-place anything. If for example you `push!` to `x` you’re actually modifying it and the caller would see it.

I think that was the point @skleinbo was trying to make about Julia’s pass-by-sharing. In contrast, in C++ you _can_ use a reference variable to modify in-place (as the second example shows).

---

<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:** [June 11, 2022, 11:52am UTC](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567/21 "2022-06-11T11:52:27Z")

</div>

> [@paulmelis](#):
>
> I think that was the point @skleinbo was trying to make about Julia’s pass-by-sharing.

But that doesn’t have anything to do with evaluation strategy, but with assignment semantic.

[Next page](https://discourse.julialang.org/t/do-c-performance-optimisations-apply-to-julia/82567.md?page=2)
