# Motoko, Array, Memory

**URL:** <https://forum.dfinity.org/t/motoko-array-memory/5324>\
**Category:** Programs & Applications\
**Created:** [June 18, 2021, 11:16am UTC](https://forum.dfinity.org/t/motoko-array-memory/5324 "2021-06-18T11:16:42Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![alexeychirkov](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/alexeychirkov/32/1991_2.png) [@alexeychirkov](https://forum.dfinity.org/u/alexeychirkov)\
**Post date:** [June 18, 2021, 11:16am UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/1 "2021-06-18T11:16:42Z")

</div>

Main purpose of my tests is to predict the memory allocation in Motoko.

I am running local replica using following command: `dfx start --no-artificial-delay --clean`

**Array of Nat8’s**

I am creating an array of 1\_000\_000 Nat8 number, so I expect that is it 1\_000\_000 \* 1 byte = 1\_000\_000 bytes = should be around 1Mb in memory.

[Source code](https://gist.github.com/alexeychirkov/c2bb6663cbb3f7030739511e1276f575)

Main part of the source code: `var store: [var Nat8] = Array.init<Nat8>(initialSize, 0);`

Execution steps:

1. `dfx deploy`
2. `dfx canister call test getCanisterMemoryInfo`  
Result:

```auto
(
  record {
    rts_max_live_size = 4_000_016;
    rts_memory_size = 8_192_000;
    rts_total_allocation = 4_000_140;
    rts_heap_size = 4_000_052;
    rts_reclaimed = 88;
    rts_version = "0.1";
  },
)

```

1. `dfx canister call test getSize`  
Result:

```auto
(1_000_000)

```

1. `dfx canister status test`  
Result:

```auto
Canister status call result for test.
Status: Running
Controller: rwlgt-iiaaa-aaaaa-aaaaa-cai
Memory allocation: 0
Compute allocation: 0
Freezing threshold: 2_592_000
Memory Size: Nat(BigUint { data: [8364189] })
Balance: 4_000_000_000_000 Cycles
Module hash: 0x4ae83d59334f4a4ca501742de29e580e1390a468225fa6db4b53310c80ef21b0

```

Observations:

1. `rts_memory_size` is 8\_192\_000 bytes
2. `Memory Size` from command line call is 8\_364\_189 bytes

So the main question is: why Array takes the same amount of memory as Array?

---

<div class="post-metadata">

**Author:** ![alexeychirkov](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/alexeychirkov/32/1991_2.png) [@alexeychirkov](https://forum.dfinity.org/u/alexeychirkov)\
**Post date:** [June 18, 2021, 11:23am UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/2 "2021-06-18T11:23:26Z")

</div>

Correct question:

1\_000\_000 big `Array<Nat8>` [(source)](https://gist.github.com/alexeychirkov/c2bb6663cbb3f7030739511e1276f575) and `Array<Nat64>` [(source)](https://gist.github.com/alexeychirkov/bd8a7afe8d4c85e3858efc59e07d5be7) takes the same amount of memory ~8Mb…  
Why `Array<Nat8>` takes so much memory?

---

<div class="post-metadata">

**Author:** ![rossberg](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/rossberg/32/795_2.png) [@rossberg](https://forum.dfinity.org/u/rossberg)\
**Post date:** [June 18, 2021, 11:35am UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/3 "2021-06-18T11:35:26Z")

</div>

The array representation has to be uniform to support generics. Hence `[Nat8]` has the same memory layout as other arrays. If you want a compact bytes array (and don’t need random access or mutation), you can use type `Blob`.

(We’ve been discussing specialised array representations, but this hasn’t been a priority yet, and they would induce some overhead into array accesses.)

---

<div class="post-metadata">

**Author:** ![alexeychirkov](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/alexeychirkov/32/1991_2.png) [@alexeychirkov](https://forum.dfinity.org/u/alexeychirkov)\
**Post date:** [June 18, 2021, 12:09pm UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/4 "2021-06-18T12:09:46Z")

</div>

Thanks for a quick response.

Follow-up question:

Another test I have is [here](https://gist.github.com/alexeychirkov/1deba9bbfa899538761bd04e7a1dbaa0)

Idea is to:

1. Initialize the empty array: `var store: [Nat] = [];`
2. Append a value to it in a loop:

```auto
Iter.iterate<Nat>(Iter.range(1, count), func(x, _index) {
    store := Array.append<Nat>(store, Array.make(x));
});

```

So I want to append 10\_000 elements to the array.

Here are results:

1. `dfx canister call test appendData 10000`

```auto
(
  10_000,
  record {
    rts_max_live_size = 41_212;
    rts_memory_size = 201_129_984;
    rts_total_allocation = 200_941_792;
    rts_heap_size = 41_248;
    rts_reclaimed = 200_900_544;
    rts_version = "0.1";
  },
)

```

1. `dfx canister status test`

```auto
Canister status call result for test.
Status: Running
Memory Size: Nat(BigUint { data: [201303567] })

```

Observations:  
Based on your reply - 10\_000 elements should allocate 10\_000 \* 8 bytes = 80\_000 bytes in memory, but what we see that now canister takes ~200\_000\_000 bytes

1. Please can you shed light on _rts\__\* values?
2. Will the canister pay for all those 200\_000\_000 bytes per second?

Thanks

---

<div class="post-metadata">

**Author:** ![rossberg](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/rossberg/32/795_2.png) [@rossberg](https://forum.dfinity.org/u/rossberg)\
**Post date:** [June 18, 2021, 1:00pm UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/5 "2021-06-18T13:00:28Z")

</div>

First of all, let me point out that constructing an array by repeated concatenation is not something you should do in production code. Since both operands need to be copied for each append, the cost of doing this is quadratic in the size of the final array.

Assuming you want an immutable array, a more appropriate way is to create the complete array as mutable, initialise it via mutation, and then freeze it.

As for the `rts` values: `memory_size` is the current size of the Wasm memory array (mostly the same as the canister memory size). This can only grow, never shrink in Wasm, even if most of it is unused. However, unused/unmodified canister memory ought to have no cost on the IC.

The other values come from [here](https://github.com/dfinity/motoko/blob/2aa5073364b43769309d4358281ce2dadb66d90e/rts/motoko-rts/src/gc.rs). The two interesting ones are probably `heap_size`, which contains the actual size of the current Motoko heap, i.e., the heap memory in use, and `max_live_size`, which is the largest heap size that has remained so far after a GC. The other numbers are accumulative total bytes allocated and collected so far, which probably is less interesting.

So your program really only uses 40K of live memory in the end. (An immutable array takes up 4 bytes per element.)

Edit: But the 200M intermediate memory size are a consequence of your loop and its quadratic cost. Before Motoko gets to run GC, you are allocating 10\_000 arrays, with an average size of 5000 elements, i.e., 20K bytes. So the heap grew to 20K \* 10\_000 = 200M before GC happened at the end of the message. And of course you are paying for this temporary memory usage, just as for the cycles all the copying costs.

---

<div class="post-metadata">

**Author:** ![alexeychirkov](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/alexeychirkov/32/1991_2.png) [@alexeychirkov](https://forum.dfinity.org/u/alexeychirkov)\
**Post date:** [June 18, 2021, 3:10pm UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/6 "2021-06-18T15:10:34Z")

</div>

Thank you @rossberg

Immutable array - clear.

Lets say I have a mutable array of Nat’s.  
I create it:

```auto
var store: [var Nat] = Array.init<Nat>(10_000, 0);

```

Then fill it with some values.  
Later I would like to increase its size by N and fill with another values.

What would be the most efficient code to do that?

---

<div class="post-metadata">

**Author:** ![rossberg](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/rossberg/32/795_2.png) [@rossberg](https://forum.dfinity.org/u/rossberg)\
**Post date:** [June 18, 2021, 3:39pm UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/7 "2021-06-18T15:39:13Z")

</div>

Arrays have a fixed size that cannot be increased. If you cannot preallocate an array of suitable size then you’ll need to create a new one and copy over.

Growable arrays are a useful abstraction, but you can build them as a data structure on top of primitive arrays, e.g., like vectors in C++. Something like (untested):

```auto
class Table<T>(n : Nat, init : T) {
  var it : [var T] = Array.init<T>(n, init);

  public func get(i : Nat) : T { it[i] };
  public func set(i : Nat, x : T) { it[i] := x };
  public func size() : Nat { it.size() }
  public func grow(m : Nat) {
    let n = it.size();
    let a = Array.init<T>(n + m, func(i) {
        if (i < n) { it[i] } else { init }
    });
    it := a;
  };
  public func setAnywhere(i : Nat, x : T) {
    let n = it.size();
    if (i >= n) { grow(2 * (i + 1) - n) };
    it[i] := x;
  }
}

```

---

<div class="post-metadata">

**Author:** ![claudio](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/claudio/32/322_2.png) [@claudio](https://forum.dfinity.org/u/claudio)\
**Post date:** [June 18, 2021, 3:42pm UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/8 "2021-06-18T15:42:11Z")

</div>

The [Buffer](https://sdk.dfinity.org/docs/base-libraries/buffer) library gives you something similar already, IIRC. [src](https://github.com/dfinity/motoko-base/blob/master/src/Buffer.mo).

---

<div class="post-metadata">

**Author:** ![skilesare](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/skilesare/32/5609_2.png) [@skilesare](https://forum.dfinity.org/u/skilesare)\
**Post date:** [June 18, 2021, 6:22pm UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/9 "2021-06-18T18:22:12Z")

</div>

So if we are sending a big data structure from one canister to another we probably want to convert it to blob first? And the reconstitute it on the other end?

---

<div class="post-metadata">

**Author:** ![rossberg](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/rossberg/32/795_2.png) [@rossberg](https://forum.dfinity.org/u/rossberg)\
**Post date:** [June 18, 2021, 6:46pm UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/10 "2021-06-18T18:46:39Z")

</div>

No, that’s not necessary. When sending a message, the data get’s serialised anyway, and the format is different (and generally more compact) than in the heap. Blob and [Nat8] actually get serialised to exactly the same wire format.

---

<div class="post-metadata">

**Author:** ![skilesare](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/skilesare/32/5609_2.png) [@skilesare](https://forum.dfinity.org/u/skilesare)\
**Post date:** [June 18, 2021, 6:57pm UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/11 "2021-06-18T18:57:16Z")

</div>

You have saved me 3 hours. May the karma gods smile upon you today.

---

<div class="post-metadata">

**Author:** ![Hazel](https://avatars.discourse-cdn.com/v4/letter/h/e56c9b/32.png) [@Hazel](https://forum.dfinity.org/u/Hazel)\
**Post date:** [July 7, 2021, 11:21pm UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/12 "2021-07-07T23:21:23Z")

</div>

> [@rossberg](#):
>
> As for the `rts` values: `memory_size` is the current size of the Wasm memory array (mostly the same as the canister memory size). This can only grow, never shrink in Wasm, even if most of it is unused. However, unused/unmodified canister memory ought to have no cost on the IC.

Bit late here - but when a canister is at rest which number is what we’re actually paying for on the IC? Heap, Memory Size, something else?

---

<div class="post-metadata">

**Author:** ![alexeychirkov](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/alexeychirkov/32/1991_2.png) [@alexeychirkov](https://forum.dfinity.org/u/alexeychirkov)\
**Post date:** [July 8, 2021, 7:21am UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/13 "2021-07-08T07:21:50Z")

</div>

as @rossberg said - I guess we are paying for `rts_max_live_size`

---

<div class="post-metadata">

**Author:** ![Hazel](https://avatars.discourse-cdn.com/v4/letter/h/e56c9b/32.png) [@Hazel](https://forum.dfinity.org/u/Hazel)\
**Post date:** [July 8, 2021, 3:43pm UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/14 "2021-07-08T15:43:11Z")

</div>

I interpreted `rts_max_live_size` as the largest size ever recorded. For instance, say we have something like:

```auto
func load (n : Nat) {
  foo := Array.tabulate<Nat8>(n func(_){123});
}

func delete () {
 foo := [];
}

```

The way I though it works was - Starting from nothing - If we call load(1\_000\_000) then

`rts_max_live_size` → 4\_000\_000  
`rts_heap_size` → 4\_000\_000

Next, we call delete() which GCs at the end of the message

`rts_max_live_size` → 4\_000\_000  
`rts_heap_size` → 0 + other memory bits…

Then if we call load(100)

`rts_max_live_size` → 4\_000\_000  
`rts_heap_size` → 400 + other memory bits…

---

<div class="post-metadata">

**Author:** ![matthewhammer](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/matthewhammer/32/323_2.png) [@matthewhammer](https://forum.dfinity.org/u/matthewhammer)\
**Post date:** [July 8, 2021, 6:55pm UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/15 "2021-07-08T18:55:36Z")

</div>

> [@Hazel](#):
>
> when a canister is at rest which number is what we’re actually paying for on the IC? Heap, Memory Size, something else?

Borrowing chat text from another engineer here who knows (based on the public replica code):

> We charge for executed instructions and dirtied pages after execution, but there is also storage fee that is taken independently from execution to prevent IC becoming a free permanent replicated storage for someone’s backups. The size of the fee is proportional to the total amount of memory occupied by the canister.

(@roman-kashitsyn wrote that in another context; borrowed with permission.)

Additionally, the GC of Motoko is a copying collector (for now, by default). That collector has its own policy for managing from/to space memory, for which you are paying cycles as well.

Some new things could mitigate that cost, and help control it:

- Using another GC policy (new `moc` version supports a compacting GC, optionally, but I don’t know myself how that would impact that cycle cost. Certainly more work will continue to focus on improving the GC overhead in various ways with respect to cycle cost).
- [A new API for stable memory](https://github.com/dfinity/motoko/pull/2626) that permits a Motoko dev to manage memory manually, if they so choose. That’s still a draft idea, but it’s related to the larger memory management discussion.

Finally, my interpretation is that [this is the relevant memory usage check](https://github.com/dfinity/motoko/blob/2aa5073364b43769309d4358281ce2dadb66d90e/rts/motoko-rts/src/gc.rs#L56) (modulo what I say above about the copying collector).

---

<div class="post-metadata">

**Author:** ![Hazel](https://avatars.discourse-cdn.com/v4/letter/h/e56c9b/32.png) [@Hazel](https://forum.dfinity.org/u/Hazel)\
**Post date:** [July 8, 2021, 7:35pm UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/16 "2021-07-08T19:35:19Z")

</div>

Thank you so much @matthewhammer !

---

<div class="post-metadata">

**Author:** ![ComputerInternetMan](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/computerinternetman/32/1848_2.png) [@ComputerInternetMan](https://forum.dfinity.org/u/ComputerInternetMan)\
**Post date:** [July 8, 2021, 10:34pm UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/17 "2021-07-08T22:34:39Z")

</div>

Thank you @matthewhammer  
2 /infty & beyond!

---

<div class="post-metadata">

**Author:** ![rossberg](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/rossberg/32/795_2.png) [@rossberg](https://forum.dfinity.org/u/rossberg)\
**Post date:** [July 9, 2021, 7:55am UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/18 "2021-07-09T07:55:57Z")

</div>

Thanks Matthew. So to highlight something from his reply, my statement from my own answer earlier,

> [@rossberg](#):
>
> However, unused/unmodified canister memory ought to have no cost on the IC.

was not actually correct. The IC will still charge you a storage fee for the _entire_ `memory_size`. I’m sorry for spreading wrong information before!

We have been discussing to extend the IC with some way for an application to signal that a certain part of the memory is “unused” (nulled out) and can hence be eliminated from storage (effectively, providing a sort of shrink through the backdoor). But that does not exist yet.

For Motoko that means that you should be even more careful not to create excessive garbage in a message, since that might temporarily blow up the heap size but Motoko has no way to “return” it.

---

<div class="post-metadata">

**Author:** ![alexeychirkov](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/alexeychirkov/32/1991_2.png) [@alexeychirkov](https://forum.dfinity.org/u/alexeychirkov)\
**Post date:** [August 14, 2021, 6:15am UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/19 "2021-08-14T06:15:59Z")

</div>

> [@rossberg](#):
>
> providing a sort of shrink

@rossberg any news regarding “providing a sort of shrink for memory”?  
I mean currently we have to pay for `memory_size`.  
Is there a way to shrink this memory size?  
Maybe canister upgrade will help? Like a restart of the PC 🙂

---

<div class="post-metadata">

**Author:** ![skilesare](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/skilesare/32/5609_2.png) [@skilesare](https://forum.dfinity.org/u/skilesare)\
**Post date:** [August 14, 2021, 12:27pm UTC](https://forum.dfinity.org/t/motoko-array-memory/5324/20 "2021-08-14T12:27:38Z")

</div>

> [@rossberg](#):
>
> No, that’s not necessary. When sending a message, the data get’s serialised anyway, and the format is different (and generally more compact) than in the heap. Blob and [Nat8] actually get serialised to exactly the same wire format.

There has been some recent discussion on discord that uploading a chunked blob is twice as fast as uploading Nat8 arrays. I’m currently writing some code to test this myself, but the theory is that allocating the array in motoko may be slower even if the serialization across the wire is faster. I’m assuming this is the same for xcanister calls but it would be great to get more validation.

[Next page](https://forum.dfinity.org/t/motoko-array-memory/5324.md?page=2)
