# Memory Pre-allocation in the global scope

**URL:** <https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861>\
**Category:** New to Julia\
**Tags:** question\
**Created:** [August 17, 2022, 7:21am UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861 "2022-08-17T07:21:07Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![NaMod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/namod/32/38102_2.png) [@NaMod](https://discourse.julialang.org/u/NaMod)\
**Post date:** [August 17, 2022, 7:21am UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/1 "2022-08-17T07:21:07Z")

</div>

I have written a code with no memory allocation. To do so, I have defined all required pieces of the memory (in the global scope) beforehand and passed them into mutating functions wherever needed.  
I was wondering whether it is conceptually correct or not. I mean, is it the normal way of avoiding memory allocations within function calls?

This is a small example of what I have implemented:

```julia
using BenchmarkTools
n=10
N=100
R1=zeros(Float64,n)
R2=zeros(Float64,n)
V=zeros(Float64,n,N);
P=zeros(Float64,n,N);
G=zeros(Float64,n);
function F1!(V::Matrix{Float64}, n::Int64, i::Int64, R1::Vector{Float64}, R2::Vector{Float64}, P::Matrix{Float64}, G::Vector{Float64})
  for j=1:n
    R1[j] = rand()
    R2[j] = rand()
  end
  @. V[:,i] = R1*(@view P[:,i]) + R2 * G
end

i=10
@btime F1!(V::Matrix{Float64}, n::Int64, i::Int64, R1::Vector{Float64}, R2::Vector{Float64}, P::Matrix{Float64}, G::Vector{Float64})

```

---

<div class="post-metadata">

**Author:** ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)\
**Post date:** [August 17, 2022, 1:12pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/2 "2022-08-17T13:12:39Z")

</div>

Using global variables is poor practice for performance reasons. You are much better off to wrap all of that in a function called “calc” or “go” or something and have it return the results. You can still preallocate within that function.

See here [Performance Tips · The Julia Language](https://docs.julialang.org/en/v1/manual/performance-tips/)

---

<div class="post-metadata">

**Author:** ![NaMod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/namod/32/38102_2.png) [@NaMod](https://discourse.julialang.org/u/NaMod)\
**Post date:** [August 17, 2022, 1:31pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/3 "2022-08-17T13:31:03Z")

</div>

So, do you mean something like this?

```julia
function F1!(V::Matrix{Float64}, n::Int64, i::Int64, R1::Vector{Float64}, R2::Vector{Float64}, P::Matrix{Float64}, G::Vector{Float64})
  for j=1:n
    R1[j] = rand()
    R2[j] = rand()
  end
  @. V[:,i] = R1*(@view P[:,i]) + R2 * G
end

function f2()
  n=10
  N=100
  R1=zeros(Float64,n)
  R2=zeros(Float64,n)
  V=zeros(Float64,n,N);
  P=zeros(Float64,n,N);
  G=zeros(Float64,n);

  i=10
  F1!(V::Matrix{Float64}, n::Int64, i::Int64, R1::Vector{Float64}, R2::Vector{Float64}, P::Matrix{Float64}, G::Vector{Float64})

end

@btime f2()

```

Maybe I’m missing something? Here is the results of the two runs:

The first implementation:

```julia
i=10
@btime F1!(V::Matrix{Float64}, n::Int64, i::Int64, R1::Vector{Float64}, R2::Vector{Float64}, P::Matrix{Float64}, G::Vector{Float64})
73.717 ns (0 allocations: 0 bytes)

```

The second implementation:

```julia
@btime f2()
1.030 μs (5 allocations: 16.30 KiB)

```

---

<div class="post-metadata">

**Author:** ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)\
**Post date:** [August 17, 2022, 1:44pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/4 "2022-08-17T13:44:41Z")

</div>

Yes, in the first case you are timing only F1! Because you already allocated the stuff before doing @btime

Another thing, in Julia we usually don’t adorn things with explicit type declarations. This may be mitigating the usual issues with using global variables since the issue is globals are not type stable.

---

<div class="post-metadata">

**Author:** ![NaMod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/namod/32/38102_2.png) [@NaMod](https://discourse.julialang.org/u/NaMod)\
**Post date:** [August 17, 2022, 1:56pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/5 "2022-08-17T13:56:38Z")

</div>

Oh, true!  
Thanks a lot.  
As the last question, do you know a reference other than Julia’s documentation where I can dig more into the efficiency tips?

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [August 17, 2022, 2:14pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/6 "2022-08-17T14:14:04Z")

</div>

Julia’s documentation ([Performance Tips](https://docs.julialang.org/en/v1/manual/performance-tips/), mainly) should really be your starting point, and I don’t think anything else is even necessary from the “theoretical” side. But from the practical side, there are some packages being developed, like [JET.jl](https://github.com/aviatesk/JET.jl), which help with automation.

Regarding your initial question, there are several packages which help with preallocation, like these two:

> **[GitHub - oxinabox/AutoPreallocation.jl: What if your code allocated less?...](https://github.com/oxinabox/AutoPreallocation.jl)**
>
> What if your code allocated less? Remember what memory we needed last time and use it again every time after - GitHub - oxinabox/AutoPreallocation.jl: What if your code allocated less? Remember wha...

> **[GitHub - FluxML/Alloc.jl](https://github.com/FluxML/Alloc.jl)**
>
> Contribute to FluxML/Alloc.jl development by creating an account on GitHub.

---

<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:** [August 17, 2022, 2:23pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/7 "2022-08-17T14:23:52Z")

</div>

Both strategies are fine, depending on what you want. Your `F1!` function is fine, and you have correctly used the approach of passing all variables as parameters of that function (this is fundamental, you do not use global variables inside the function). Then, you are benchmarking the performance of the `F1!`. This is the usual workflow when your function does “the job” and you will use it with different data in an interative environment.

Defining the `f2` as above helps in particular if you are developing that code using `Revise`, and changing the content of `f2`, then you can just update it and rerun it to obtain new results.

---

<div class="post-metadata">

**Author:** ![NaMod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/namod/32/38102_2.png) [@NaMod](https://discourse.julialang.org/u/NaMod)\
**Post date:** [August 17, 2022, 2:34pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/8 "2022-08-17T14:34:36Z")

</div>

In my application, I need to run `F1!`, along with many other functions, many times. So, I reserved some pieces of memory and overwrote them in each iteration. Please correct me if I’m wrong. In the first approach, the variables are defined in the global scope, but when I pass them into a function, overwriting them doesn’t cause performance issues. Is that correct?

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [August 17, 2022, 2:35pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/9 "2022-08-17T14:35:02Z")

</div>

> [@NaMod](#):
>
> ```julia
> for j=1:n
> R1[j] = rand()
> R2[j] = rand()
> end
> @. V[:,i] = R1*(@view P[:,i]) + R2 * G
> 
> ```

You could just do

```julia
@. V[:,i] = rand()*(@view P[:,i]) + rand() * G

```

and eliminate the `R1` and `R2` arrays completely. The `@.` macro will turn `rand()` into `rand.()`, which will call it once per element.

(Also note that there is typically no need to pass array sizes like `n` into a function, because you get get them from `size` or `length`.)

Also, note that your type signatures are way stricter than needed, unnecessarily reducing the generality of your function with _no_ benefit in performance. See [argument-type declarations](https://docs.julialang.org/en/v1/manual/functions/#Argument-type-declarations).

---

<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:** [August 17, 2022, 2:35pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/10 "2022-08-17T14:35:58Z")

</div>

I think there is nothing wrong with const global variables as long as they are containers of concrete types.

Even better is the use of a const global struct that contains all your pre-allocated arrays. Pass it as first parameter to your worker function and you are done…

So either this:

```julia
n=10
N=100
const R1=zeros(Float64,n)
const R2=zeros(Float64,n)
const V=zeros(Float64,n,N)
const P=zeros(Float64,n,N)
const G=zeros(Float64,n)

```

or this:

```julia
using Parameters
n=10
N=100
@with_kw struct Mem
    R1::Vector{Float64}=zeros(Float64,n)
    R2::Vector{Float64}=zeros(Float64,n)
    V::Matrix{Float64}=zeros(Float64,n,N)
    P::Matrix{Float64}=zeros(Float64,n,N)
    G::Vector{Float64}=zeros(Float64,n)
end 
const mem=Mem()

```

---

<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:** [August 17, 2022, 2:41pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/11 "2022-08-17T14:41:35Z")

</div>

> [@NaMod](#):
>
> but when I pass them into a function, overwriting them doesn’t cause performance issues. Is that correct?

yes, correct. And doing what you do is the most easy way to benchmark your functions independently, if they are to be used within a more complex code. We do that all the time.

(ps: I’m not sure what you mean by “overwriting” here, but what you did there is fine)

---

<div class="post-metadata">

**Author:** ![NaMod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/namod/32/38102_2.png) [@NaMod](https://discourse.julialang.org/u/NaMod)\
**Post date:** [August 17, 2022, 2:44pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/12 "2022-08-17T14:44:17Z")

</div>

Cool! Thanks for the tips! I do have a question regarding the second one, though. Do not `size` or `length` cause temporary allocation inside the function?

---

<div class="post-metadata">

**Author:** ![NaMod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/namod/32/38102_2.png) [@NaMod](https://discourse.julialang.org/u/NaMod)\
**Post date:** [August 17, 2022, 2:53pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/13 "2022-08-17T14:53:01Z")

</div>

> [@stevengj](#):
>
> `@. V[:,i] = rand()*(@view P[:,i]) + rand() * G`

Actually, it made the code faster:

```julia
49.192 ns (0 allocations: 0 bytes)

```

---

<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:** [August 17, 2022, 2:55pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/14 "2022-08-17T14:55:46Z")

</div>

If the variables are passed to the function as parameters, there no need for them to be `const`ants.

Making them constants can make the testing of the function annoying, if you want to change their values.

> [@NaMod](#):
>
> Does not `size` or `length` cause temporary allocation inside the function?

no, they don’t

In summary, that specifically you can write as:

```julia
julia> function F1!(V, P, G, i)
         @. V[:,i] = rand()*(@view P[:,i]) + rand() * G
       end
F1! (generic function with 1 methods)

```

and test it with:

```julia
julia> n=10; N=100; V=zeros(n,N); P=zeros(n,N); G=zeros(n); i=10;

julia> @btime F1!($V,$P,$G,$i)
  30.811 ns (0 allocations: 0 bytes)

```

(also note the `$` in the call to `@btime`, that is required)

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [August 17, 2022, 3:13pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/15 "2022-08-17T15:13:59Z")

</div>

> [@lmiq](#):
>
> > [@NaMod](#):
> >
> > Does not `size` or `length` cause temporary allocation inside the function?
> 
> no, they don’t

In particular, when we talk about “allocations” we mean _heap allocations_, which are generally only needed to create (or enlarge) variable-sized containers like `Array` or `Dict`, or to create `mutable struct` objects. Allocating such blocks of memory requires an expensive system call (think `malloc` in C).

In contrast, immutable values like numbers (except bignums), tuples, and `struct` objects can be stored much more cheaply, e.g. in stack or CPU-register memory, with no system call, so one doesn’t typically worry about the performance cost of “allocating” them.

---

<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:** [August 17, 2022, 4:37pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/16 "2022-08-17T16:37:37Z")

</div>

> [@lmiq](#):
>
> Making them constants can make the testing of the function annoying, if you want to change their values.

This is not true. If you have a definition like

```julia
const R1=zeros(10)

```

you can change the values, e.g.

```julia
R1[1]=10

```

The only thing you cannot do is to change the type.

---

<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:** [August 17, 2022, 4:39pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/17 "2022-08-17T16:39:57Z")

</div>

Thanks for clarifying that, I meant “values” is a more general sense, as if one wanted to change to which array the variable label is bound to. There is that subtlety:

```julia
julia> const x = [1,2];

julia> y = [3,4]; # same type of x

julia> x = y
WARNING: redefinition of constant x. This may fail, cause incorrect answers, or produce other errors.
2-element Vector{Int64}:
 3
 4

```

---

<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:** [August 17, 2022, 5:09pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/18 "2022-08-17T17:09:14Z")

</div>

Well, this works:

```julia
const x = [1,2]
y = [3,4]
x .= y

```

Yes, you have to add a dot… not so difficult, is it?

---

<div class="post-metadata">

**Author:** ![Nathan\_Boyer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nathan_boyer/32/14825_2.png) [@Nathan\_Boyer](https://discourse.julialang.org/u/Nathan_Boyer)\
**Post date:** [August 17, 2022, 5:39pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/19 "2022-08-17T17:39:27Z")

</div>

Let me try to summarize (because there is a lot of confusion around this):

1. **Your initial approach is fine in terms of performance.**  
“Avoid global variables” means avoid inheriting global variables through scoping. Your code will use global variables as inputs and outputs; this is normal.  
`F1!()` could have been defined without taking any arguments and still work. Then it would have to obtain the values from the outer (in this case global) scope. That would be bad practice and slow unless those variables are declared to be `const`. The `const` approach is intended for actual constants that are fixed across all simulations (none of your variables). It is useful because `const` variables are inherently known to all functions and are still fast to use. [Link](https://docs.julialang.org/en/v1/manual/variables-and-scoping/#Constants)

2. **You don’t need to allocate `R1` and `R2` in your example. Just call `rand` directly.**  
`F1!(V, P, G, i) = @. V[:,i] = rand()*(@view P[:,i]) + rand() * G`

3. **You could better organize your function inputs to be more readable.**

- Do not pass array sizes as arguments. `size` is very cheap to compute, so passing `n` and `N` alongside the arrays themselves just makes your code harder to read and more error prone. You should write the loop (no longer needed) as `for j in eachindex(G)`. Then you don’t need `n` at all: [link](https://discourse.julialang.org/t/why-and-when-to-pass-variables-to-sub-functions/49073).
- Some consider the type declarations unnecessary fluff: [link](https://www.oxinabox.net/2020/04/19/Julia-Antipatterns.html#over-constraining-argument-types). They don’t practically do anything besides document and throw better errors. This topic often causes [heated arguments](https://discourse.julialang.org/t/why-specify-argument-types-and-return-types/55567) though.
- A `setup` function for allocating the arrays would clean up your code, but it isn’t performance necessary.

```julia
function setup(n, N)
    V = zeros(Float64, n, N)
    P = zeros(Float64, n, N)
    G = zeros(Float64, n)
    return V, P, G
end
function compute!(V, P, G, i)
    @. V[:,i] = rand()*(@view P[:,i]) + rand() * G
end

n = 10
N = 100
i = 10
V, P, G = setup(n, N)
compute!(V, P, G, i)

```

- A custom type `struct` could further clean up your code. Though I personally think the previous version is good enough.

```julia
struct Parameters
    V::Matrix{Float64}
    P::Matrix{Float64}
    G::Vector{Float64}
end
function setup(n, N)
    V = zeros(Float64, n, N)
    P = zeros(Float64, n, N)
    G = zeros(Float64, n)
    return Parameters(V, P, G)
end
function compute!(data, i)
    @. data.V[:,i] = rand()*(@view data.P[:,i]) + rand() * data.G
end

n = 10
N = 100
i = 10
data = setup(n, N)
compute!(data, i)

```

- Typically in Julia, the `function` and `struct` definitions (top half of above code) would be stored in a separate package or file from the function calls (bottom half of above code) for better organization: [link](https://docs.julialang.org/en/v1/manual/code-loading/). Your original code has function and variable definitions all mixed together which makes it hard to read.

---

<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:** [August 17, 2022, 5:44pm UTC](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861/20 "2022-08-17T17:44:17Z")

</div>

> [@ufechner7](#):
>
> Yes, you have to add a dot… not so difficult, is it?

No, but it means a different thing.

[Next page](https://discourse.julialang.org/t/memory-pre-allocation-in-the-global-scope/85861.md?page=2)
