# Non-Deterministic Memory Allocation?

**URL:** <https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941>\
**Category:** General Usage\
**Tags:** question\
**Created:** [October 13, 2023, 11:05pm UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941 "2023-10-13T23:05:25Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![mcmuffin6o](https://avatars.discourse-cdn.com/v4/letter/m/9f8e36/32.png) [@mcmuffin6o](https://discourse.julialang.org/u/mcmuffin6o)\
**Post date:** [October 13, 2023, 11:05pm UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/1 "2023-10-13T23:05:25Z")

</div>

After running a short script several times without making changes (using `include("script.jl");` from the REPL), I noticed that the number and total size of memory allocations was not constant. How (and why?) can this happen? I’ve attached the script and several samples of output for reference.

`startup.jl`:

```julia
module StartupUtils
export @showc

using Revise;
using LinearAlgebra;
using Plots;

# Compact show
macro showc(exs...)
    blk = Expr(:block)
    for ex in exs
        push!(blk.args, :(println($(sprint(Base.show_unquoted,ex)*" = "),
        repr(begin local value = $(esc(ex)) end, context = :compact=>true))))
    end
    isempty(exs) || push!(blk.args, :value)
    return blk
end

end

using .StartupUtils;

```

`script.jl`:

```julia
module HW6
using Revise

using .Main: @showc

function T1(θ)
    return [
        1 0 0
        0 cos(θ) sin(θ)
        0 -sin(θ) cos(θ)
    ]
end

function T2(θ)
    return [
        cos(θ) 0 -sin(θ)
        0 1 0
        sin(θ) 0 cos(θ)
    ]
end

const IcB = [
    34 1 6
     1 15 3
     6 3 10
] # kg m^2

const ψ = deg2rad(5)
const θ = deg2rad(10)
const ϕ = deg2rad(-3)

const ψdot = deg2rad(-1)
const θdot = deg2rad(1)
const ϕdot = deg2rad(4)

const ω = T1(ϕ) * T2(θ) * [0, 0, ψdot] .+ T1(ϕ) * [0, θdot, 0] .+ [ϕdot, 0, 0]

function main()
    T_rot = 1/2 * ω' * IcB * ω
    @showc T_rot
end

@time "Total" main()

end

```

Samples of output:

```julia
WARNING: replacing module HW6.
T_rot = 0.0873849
Total: 0.000079 seconds (19 allocations: 960 bytes)

```

```julia
WARNING: replacing module HW6.
T_rot = 0.0873849
Total: 0.000097 seconds (21 allocations: 1.797 KiB)

```

```julia
WARNING: replacing module HW6.
T_rot = 0.0873849
Total: 0.000050 seconds (19 allocations: 960 bytes)

```

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [October 14, 2023, 7:55am UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/2 "2023-10-14T07:55:11Z")

</div>

> [@mcmuffin6o](#):
>
> `WARNING: replacing module HW6.`

This looks like you’re reincluding the file over and over again? In which case, it’s very possible that some runs include GC time where things that weren’t cleaned up before are cleaned up now.

–

I’ve moved this topic to `General Usage`, since this is not really a question about the internals of the language or runtime.

---

<div class="post-metadata">

**Author:** ![cstjean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cstjean/32/1444_2.png) [@cstjean](https://discourse.julialang.org/u/cstjean)\
**Post date:** [October 14, 2023, 6:01pm UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/3 "2023-10-14T18:01:23Z")

</div>

Memory allocations does fluctuate for complex function calls, and IMO it’s a good question about internals. Here is the histogram for `[@allocated Pkg.status() for _ in 1:50]`

 ![image](https://global.discourse-cdn.com/julialang/original/3X/5/0/5086b1fb452c2192cd722ac0397e6aa81a25120e.png)

Which operation causes this variation? `GC.enable(false)` doesn’t change anything.

---

<div class="post-metadata">

**Author:** ![mcmuffin6o](https://avatars.discourse-cdn.com/v4/letter/m/9f8e36/32.png) [@mcmuffin6o](https://discourse.julialang.org/u/mcmuffin6o)\
**Post date:** [October 14, 2023, 6:42pm UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/4 "2023-10-14T18:42:12Z")

</div>

Putting everything into a module that gets reloaded each run is the only reliable way I’ve found to achieve deterministic program output in Julia. Otherwise, you get random crap based on whatever you last did at the REPL, since there’s no way to clear the workspace!

As a side note, if I tried to recommend Julia in its current state to a bunch of MATLAB junkies, I’d get laughed out of the room as soon as they ask about whether or not it’s possible to do something as trivial as _clearing the workspace_. I’m sure it’s a difficult problem or whatever ([just like Rust’s inability to perform compile-time arithmetic on type parameters](https://users.rust-lang.org/t/using-generic-parameters-in-const-operations/96651?u=mcmuffin6o)), but in both cases these are basic features that you really start to miss when they’re taken away.

Regardless, the call to `@time` is only being given the `main` function, so I don’t understand why it would be tracking allocations unrelated to `main`.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [October 14, 2023, 6:57pm UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/5 "2023-10-14T18:57:20Z")

</div>

> [@mcmuffin6o](#):
>
> Putting everything into a module that gets reloaded each run is the only reliable way I’ve found to achieve deterministic program output in Julia. Otherwise, you get random crap based on whatever you last did at the REPL, since there’s no way to clear the workspace!

Do you know about [BenchmarkTools.jl](https://juliahub.com/ui/Packages/General/BenchmarkTools)? Most Julia users use that package for benchmarking. It takes care of all the finicky precompilation, statistic collection & aggregation and, yes, also managing the GC to make the results as deterministic as possible.

There are a number of reasons why allocations or timing of a benchmark can fluctuate; first of which compilation time, how much your system is otherwise loaded, how much GC was loaded with previous values…

> [@mcmuffin6o](#):
>
> Regardless, the call to `@time` is only being given the `main` function, so I don’t understand why it would be tracking allocations unrelated to `main`.

`@time` isn’t tracking anything actively. It’s an extremely naive macro, literally only reporting the differences in time & GC statistics during its invocation:

```julia
julia> @macroexpand @time 1+1
quote
    #= timing.jl:272 =#
    begin
        #= timing.jl:277 =#
        $(Expr(:meta, :force_compile))
        #= timing.jl:278 =#
        local var"#1#stats" = Base.gc_num()
        #= timing.jl:279 =#
        local var"#3#elapsedtime" = Base.time_ns()
        #= timing.jl:280 =#
        Base.cumulative_compile_timing(true)
        #= timing.jl:281 =#
        local var"#4#compile_elapsedtimes" = Base.cumulative_compile_time_ns()
        #= timing.jl:282 =#
        local var"#2#val" = $(Expr(:tryfinally, :(1 + 1), quote
    var"#3#elapsedtime" = Base.time_ns() - var"#3#elapsedtime"
    #= timing.jl:284 =#
    Base.cumulative_compile_timing(false)
    #= timing.jl:285 =#
    var"#4#compile_elapsedtimes" = Base.cumulative_compile_time_ns() .- var"#4#compile_elapsedtimes"
end))
        #= timing.jl:287 =#
        local var"#5#diff" = Base.GC_Diff(Base.gc_num(), var"#1#stats")
        #= timing.jl:288 =#
        local var"#6#_msg" = Base.nothing
        #= timing.jl:289 =#
        Base.time_print(Base.stdout, var"#3#elapsedtime", (var"#5#diff").allocd, (var"#5#diff").total_time, Base.gc_alloc_count(var"#5#diff"), Base.first(var"#4#compile_elapsedtimes"), Base.last(var"#4#compile_elapsedtimes"), true; msg = var"#6#_msg")
        #= timing.jl:290 =#
        var"#2#val"
    end
end

```

So whatever bookkeeping the GC happens to do during a GC run that occurs in the code your `@time` invocation encompasses is recorded too.

That’s also why its docstring recommends `BenchmarkTools.jl` for bechmarking:

```julia
help?> @time
  @time expr
  @time "description" expr

[...]

  │ Note
  │
  │ For more serious benchmarking, consider the @btime macro from the BenchmarkTools.jl package which among other
  │ things evaluates the function multiple times in order to reduce noise.

```

---

<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:** [October 15, 2023, 2:30am UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/6 "2023-10-15T02:30:41Z")

</div>

> [@mcmuffin6o](#):
>
> As a side note, if I tried to recommend Julia in its current state to a bunch of MATLAB junkies, I’d get laughed out of the room as soon as they ask about whether or not it’s possible to do something as trivial as _clearing the workspace_.

MATLAB’s [`clear`](https://www.mathworks.com/help/matlab/ref/clear.html?searchHighlight=clear&s_tid=srchtitle_support_results_1_clear)/[`clearvars`](https://www.mathworks.com/help/matlab/ref/clearvars.html#bt3lb2_-2_1) is made feasible by MATLAB’s atypical [workspace system](https://www.mathworks.com/help/matlab/apiref/matlab.engine.workspacetype.html#) of storing variables. This links to a [somewhat old answer](https://www.mathworks.com/matlabcentral/answers/99602-how-can-i-use-global-variables-and-matlab-workspaces#answer_108950), but it’s a more detailed description of workspaces and their unique caveats.

This just doesn’t translate to languages with modules holding their own global variables, so this feature will never be fully replicated, if at all. Let’s take a simple case where you fire up the REPL and work in `Main`. After polluting the namespace with a bunch of variables and data, you decide to remove them. First problem: you want to keep the already compiled functions; okay, we’ll keep any variable assigned to an instance of `Function`. Second problem, you don’t want to remove `const` variables because they were inlined into your compiled functions and can’t be safely reassigned anymore; fine, let’s keep them too. Third problem, the functions relying on non-`const` global variables now don’t work, and you have to manually redefine them from memory. And now you realize that MATLAB risks easier naming conflicts by throwing global variables into their own workspace so that `clear ...` doesn’t easily cause such issues (though `clear global ...` and `clear all` do remove things from the global workspace and is _discouraged_ even in `clear`’s documentation).

In most languages, the repeatable unit of code is a function, and that should be used instead of tweaking and rerunning scripts. This practice is recommended in [MATLAB](https://www.mathworks.com/help/matlab/matlab_prog/techniques-for-improving-performance.html) as [well](https://www.mathworks.com/matlabcentral/answers/272064-difference-between-clear-and-clear-all#comment_348654) by the real junkies.

---

<div class="post-metadata">

**Author:** ![mcmuffin6o](https://avatars.discourse-cdn.com/v4/letter/m/9f8e36/32.png) [@mcmuffin6o](https://discourse.julialang.org/u/mcmuffin6o)\
**Post date:** [October 20, 2023, 10:31pm UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/7 "2023-10-20T22:31:11Z")

</div>

The last link you provided explains it all:

> Advanced MATLAB users tend to write functions, not scripts.

The average MATLAB junkie doesn’t. The average MATLAB junkie has no idea what you’re talking about.

As a side note, I have tried working from the REPL several times. It’s never as good as running a script. I prefer deterministic programs – not ones that assume I’m going to type the same exact sequence of functions in perfectly every time. I seem to recall some program type that was really good for executing sequences of functions exactly the same each time…

Regardless, I’ve come up with a nuclear option that prevents any and all shenanigans. Not only does it wrap everything in a function as you recommend, it wraps those functions in a module that gets reloaded every time I source the file! Pretty sweet for when I would like to write deterministic software (which is always):

```julia
module ModuleName
using Revise

using .Main: @showp, @showc, nearly_equal

# Const variables go here

function computation()
    # Computations go here
    # Return a named tuple containing the results e.g.
    return (a=a, b=b)
end

function plotting(results)
    # Plotting goes here
    # Plot members of the named tuple e.g.
    plot(results.a, results.b)
end

function main()
    results = @time "Computation" computation()
    @time "Plotting" plotting(results)
end

@time "Total" main()

end

```

(`@showp`, `@showc`, and `nearly_equal` are macros/functions that I define in my `startup.jl`)

---

<div class="post-metadata">

**Author:** ![fatteneder](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fatteneder/32/33991_2.png) [@fatteneder](https://discourse.julialang.org/u/fatteneder)\
**Post date:** [October 20, 2023, 10:49pm UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/8 "2023-10-20T22:49:33Z")

</div>

Your way of using Modules does not mesh with Revise:

- `Revise.jl` doesn’t go into the module you want to develop, instead it should be used from the REPL.
- You should not put a function call like `main()` inside a module and then re-include that module for the sake of running `main`.

One way to correctly use `Revise.jl` is:

```julia
# put this into a file `main.jl`
module ModuleName

using .Main: @showp, @showc, nearly_equal

# Const variables go here

function computation()
    # Computations go here
    # Return a named tuple containing the results e.g.
    return (a=a, b=b)
end

function plotting(results)
    # Plotting goes here
    # Plot members of the named tuple e.g.
    plot(results.a, results.b)
end

function main()
    results = @time "Computation" computation()
    @time "Plotting" plotting(results)
end

end

```

Then fire up a REPL and run

```julia-repl
julia> using Revise

julia> includet("main.jl") # you do this once in a fresh REPL session; note the 't' at the end of includet!

julia> ModuleName.main()

```

Then edit any functions inside `main.jl`, go back to the REPL and run `ModuleName.main()`, rinse and repeat.

This should be deterministic enough!

See the `Revise.jl` docs for more infos: [Home · Revise.jl](https://timholy.github.io/Revise.jl/stable/)

---

<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:** [October 20, 2023, 11:26pm UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/9 "2023-10-20T23:26:17Z")

</div>

I’d also note that if you wrap everything in functions, like you did there, wrapping everything in the module is not necessary. Using `Revise`, `includet` and just calling the functions with appropriate input is enough.

---

<div class="post-metadata">

**Author:** ![mcmuffin6o](https://avatars.discourse-cdn.com/v4/letter/m/9f8e36/32.png) [@mcmuffin6o](https://discourse.julialang.org/u/mcmuffin6o)\
**Post date:** [October 20, 2023, 11:34pm UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/11 "2023-10-20T23:34:27Z")

</div>

What if I am editing a function in another module that I’m using? There have been multiple instances where I’ve edited a function in another module only for the code to call the old definition of the function because I’ve changed the call signature or the types within the call signature.

It stopped happening when I started wrapping my scripts in a module.

---

<div class="post-metadata">

**Author:** ![mcmuffin6o](https://avatars.discourse-cdn.com/v4/letter/m/9f8e36/32.png) [@mcmuffin6o](https://discourse.julialang.org/u/mcmuffin6o)\
**Post date:** [October 20, 2023, 11:39pm UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/12 "2023-10-20T23:39:17Z")

</div>

Is there any reason not to do it the way that I’ve done it? I don’t really care about how it _should_ be done; I just care that it works.

---

<div class="post-metadata">

**Author:** ![fatteneder](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fatteneder/32/33991_2.png) [@fatteneder](https://discourse.julialang.org/u/fatteneder)\
**Post date:** [October 20, 2023, 11:54pm UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/13 "2023-10-20T23:54:14Z")

</div>

Reincluding modules gives a `WARNING` and those are usually added for a reason. But I don’t know what problems that can cause.

Also: `using Revise` in your script will have not effect, you can essentially uninstall it.

---

<div class="post-metadata">

**Author:** ![mcmuffin6o](https://avatars.discourse-cdn.com/v4/letter/m/9f8e36/32.png) [@mcmuffin6o](https://discourse.julialang.org/u/mcmuffin6o)\
**Post date:** [October 21, 2023, 12:17am UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/14 "2023-10-21T00:17:14Z")

</div>

I see. Thanks for the help!

Could I put `using Revise` in my `startup.jl` to avoid typing it every time I fire up a REPL?

---

<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:** [October 21, 2023, 12:18am UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/15 "2023-10-21T00:18:58Z")

</div>

There is nothing wrong in using a module, but then use revise like:

```julia-repl
julia> using Revise # better be in startup.jl

julia> includet("file_that_contains_module.jl")

julia> using .MyModule

julia> MyModule.f() # run module function

```

then, if you modify the file that contains the module, and the definition of function `f`, it will be automatically updated. Do not `include` the file twice.

---

<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:** [October 21, 2023, 1:17am UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/16 "2023-10-21T01:17:22Z")

</div>

> [@mcmuffin6o](#):
>
> possible to do something as trivial as _clearing the workspace_. I’m sure it’s a difficult problem or whatever ([just like Rust’s inability to perform compile-time arithmetic on type parameters](https://users.rust-lang.org/t/using-generic-parameters-in-const-operations/96651?u=mcmuffin6o)), but in both cases these are basic features that you really start to miss when they’re taken away.

If you really want to reset everything also you can call exec (in Linux at least).

> [@How to exit a Julia program without completely exit REPL or shutting down Julia?](https://discourse.julialang.org/t/how-to-exit-a-julia-program-without-completely-exit-repl-or-shutting-down-julia/66655/13):
>
> I mean, it would be pretty trivial to provide this through ccall right? I imagine I could do it this afternoon if I took the time to read up on ccall. But it would be better to be a package kind of maintained by some github organization, like JuliaStrings etc except with a “OS interaction” theme or something. I just tested this on my computer, and it worked: julia\> ccall(:execvp,Int32,(Cstring,Array{Cstring}),"/home/dlakelan/julia-latest/bin/julia",Cstring[]) Also this: julia\> ccall(:execvp,…

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [October 21, 2023, 1:18am UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/17 "2023-10-21T01:18:25Z")

</div>

Revise in your startup is a very good idea. (I also would recommend adding BenchmarkTools and Test).

---

<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:** [October 21, 2023, 3:37am UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/18 "2023-10-21T03:37:04Z")

</div>

> [@mcmuffin6o](#):
>
> Regardless, I’ve come up with a nuclear option that prevents any and all shenanigans. Not only does it wrap everything in a function as you recommend,

Your `const` variables aren’t wrapped in a function, but if they have a fixed value for a reason like physical constants so you’ll never have to manually change and remember them to understand the program’s behavior, that would be fine.

> [@mcmuffin6o](#):
>
> it wraps those functions in a module that gets reloaded every time I source the file!

> [@mcmuffin6o](#):
>
> Is there any reason not to do it the way that I’ve done it? I don’t really care about how it _should_ be done; I just care that it works.

It throws away a whole global scope and requires recompilation for relatively minor changes. That’s not to say it’s always a mistake, in fact Pluto.jl does this to implement its live-coding environment. However, it uses up a lot of memory, and it is overkill for resolving the issues at hand:

> [@mcmuffin6o](#):
>
> Putting everything into a module that gets reloaded each run is the only reliable way I’ve found to achieve deterministic program output in Julia. Otherwise, you get random crap based on whatever you last did at the REPL, since there’s no way to clear the workspace!

> [@mcmuffin6o](#):
>
> As a side note, I have tried working from the REPL several times. It’s never as good as running a script. I prefer deterministic programs – not ones that assume I’m going to type the same exact sequence of functions in perfectly every time.

Again, the typical solution to this is to 1) turn as many global variables into local variables in functions as possible, and 2) feed data into functions with arguments, not by accessing declared global variables. Local variables can’t pollute the global scope and are destroyed at the end of a local scope, like a `let` block or a function call; same deal with function workspaces in MATLAB, as the old Mathworks Support answer says. Inputting data as arguments lets you keep the behavior of a method consistent and under your control rather than at the mercy of some global variables whose values you can’t quite keep track of. The Mathworks answer may have called this a practice of “advanced users” but it’s actually fairly basic for structured programming and doesn’t involve any fancy memory schemes like module replacement.

If you really don’t feel like refactoring and want to stick with scripts that do a lot in the global scope, another way to avoid global scope pollution is to just restart the process each time. Instead of repeating `include("script.jl")` in 1 REPL session, you run a [`julia script.jl`](https://docs.julialang.org/en/v1/manual/command-line-interface/) in a command prompt. Again, it’s inefficient, and in more ways than throwing away modules, but diverging from good practices isn’t ever going to be convenient.

> [@mcmuffin6o](#):
>
> I noticed that the number and total size of memory allocations was not constant. How (and why?) can this happen?

Heap allocation performance depends on heap state, and allocations can happen without your explicit intention depending on hidden state e.g. vectors reallocating for resizing. Refactoring code as function code would help as you’re not doing as drastic changes, but sometimes it’s not possible to micromanage your code to the point that the same number of allocations happen every time. Even if you do, garbage collection is out of your control and affects runtimes anyway.

---

<div class="post-metadata">

**Author:** ![mcmuffin6o](https://avatars.discourse-cdn.com/v4/letter/m/9f8e36/32.png) [@mcmuffin6o](https://discourse.julialang.org/u/mcmuffin6o)\
**Post date:** [October 22, 2023, 7:25pm UTC](https://discourse.julialang.org/t/non-deterministic-memory-allocation/104941/19 "2023-10-22T19:25:10Z")

</div>

> [@Benny](#):
>
> Your `const` variables aren’t wrapped in a function, but if they have a fixed value for a reason like physical constants so you’ll never have to manually change and remember them to understand the program’s behavior, that would be fine.

Good catch! I’ll have to fix this – I was using const variables for things that could change as I adjust my algorithm, assuming that it might be more efficient if that lets the compiler know they can go directly into the executable. These should definitely be in `main`.
