Region API 8mb stable overhead per region?

Hey there,

working with the new Region API from Motoko I’m observing a strange 8mb stable storage overhead per Region.new() call. This seems surprising to me and I wanted to check if this is known ok, but undocumented behaviour or potentially a bug.

To reproduce the issue i’ve created this region.bench.mo mops benchmark file. It’s very straight forward and just tries to allocate regions and write 100 bytes into the first page of each region:

import Bench "mo:bench";
import Nat "mo:base/Nat";
import Nat8 "mo:base/Nat8";
import Nat32 "mo:base/Nat32";
import Iter "mo:base/Iter";
import Array "mo:base/Array";
import Blob "mo:base/Blob";
import Region "mo:base/Region";

module {
  public func init() : Bench.Bench {
    let bench = Bench.Bench();

    bench.name("Test the regions");
    bench.rows(["Regions"]);
    bench.cols(["1", "10", "100", "1000", "10000"]);

    bench.runner(func(row, col) {
      let ?n = Nat.fromText(col);
      
      for (i in Iter.range(0, n - 1)) {
        var reg = Region.new();
        let _ = Region.grow(reg, 1);
        assert Region.size(reg) == 1;
        Region.storeBlob(reg, 0, make_blob(100, i));
      };
    });

    bench;
  };

  private func make_blob(size : Nat, n : Nat) : Blob {
    let a = Array.tabulate<Nat8>(size, func i = Nat8.fromIntWrap(Nat.bitshiftRight(n, 8 * Nat32.fromIntWrap(i))));
    return Blob.fromArray(a);
  };
};

Now running this surprisingly reports straight out 8mb for every single created region. E.g. 8mb for 1, 80mb for 10, 800 for 100. And then the script crashes.

$ mops bench region
Benchmark files:
• bench/region.bench.mo

==================================================

Starting dfx replica...
Deploying canisters...

——————————————————————————————————————————————————

Running bench/region.bench.mo...

| :------ | ----: | ----: | ----: | ---: | ----: |
| Regions | 244 B | 244 B | 244 B |      |       |


Stable Memory

|         |     1 |     10 |     100 | 1000 | 10000 |
| :------ | ----: | -----: | ------: | ---: | ----: |
| Regions | 8 MiB | 80 MiB | 800 MiB |      |       |


Garbage Collection

|         |      1 |       10 |       100 | 1000 | 10000 |
| :------ | -----: | -------: | --------: | ---: | ----: |
| Regions | 1004 B | 6.01 KiB | 56.28 KiB |      |       |

Unexpected error. Stopping replica...

Is this known behavior of the Region API or am I doing something wrong?

Also note that this run here crashes when trying to create 10k regions for 8gb of stable memory - this is also not clear to me (I assume it should be possible to go up to 400gb of stable storage) but I might imagine this is a local dfx instance configuration. Still any pointers would be appreciated.

Thanks!

IRCC this is actually expected behaviour.
Although you can grow a region in increments of 64k Wasm pages, the implementation actually chunks these allocations into large allocations of 128 pages. So each region will occupy at least 128 pages (8MiB), even if it was only grown by 1 page.

(The doc mentions that in this section:
Stable regions | Internet Computer)

There is also a hard-limit (–max-stable-pages) set at compile time, that limits how many physical stable memory pages your canister can use (I think that’s set to 4GB by default to allow another 4GB for stable variable storage).

Note that regions are not garbage collected and cannot be freed, so you should only use them sparingly. With the new enhanced orthogonal persistence feature now in beta,
you can probably avoid using them at all.

1 Like

Thanks, that’s what I was looking for. I must have skipped over the 128 pages pre-allocation. Regarding EOP, I’m wondering whether or how much of my project I should change now (planning production use in 2 month) –

From the linked post:

The IC will initially only offer a limited capacity of the 64-bit main memory, e.g. 4GB or 6GB. This will be gradually increased in the future to approach the same capacity as stable memory.

Is there a timeline on this? More specifically to use/store 100gb is that going to be immediately available November/December through EOP or should I keep going with the Regions API for a beginning of December canister to production release?

As I know that software development usually takes longer than planned my current plan is to keep building on the Region API - that seems the timeline safe option.

Cheers!

Hi Dominic,
I believe it will take longer than 2 months to get 100GB of EOP persistent main memory. My estimate is that by beginning of December, EOP is still in beta testing phase. This may be concluded by Q1 2025. Moreover, there are some challenges on the IC side to support larger 64-bit main memory capacity.
So, if you plan a production release relatively soon, Regions is currently most probably the only way to go.

2 Likes