Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple stable memory regions (and a new primitive type Region). #3768

Merged
merged 384 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
384 commits
Select commit Hold shift + click to select a range
02c4abe
cargo fmt
matthewhammer Mar 22, 2023
e841f89
integrate generational GC with new global in RTS for region0.
matthewhammer Mar 22, 2023
bac5505
integrate mark-compact GC with new global in RTS for region0.
matthewhammer Mar 22, 2023
aeb5a3b
remove println; fix test logic.
matthewhammer Mar 22, 2023
2718a04
impl StableMem.set_mem_size so that the compiler codegen destabilize.
matthewhammer Mar 23, 2023
17f13b3
clear the special value that we need to keep between upgrades.
matthewhammer Mar 23, 2023
109c796
nit
matthewhammer Mar 23, 2023
07ec568
revert classic unit test, stable-mem-nat8
matthewhammer Mar 23, 2023
e068c14
fix region0_load_blob
matthewhammer Mar 23, 2023
e95548d
full region API exposed through compiler.
matthewhammer Mar 23, 2023
2d69338
remove scratch file.
matthewhammer Mar 23, 2023
2643f7c
nits.
matthewhammer Mar 23, 2023
78c5f24
prints for debugging and determining skewed vs unskewed.
matthewhammer Mar 24, 2023
456e7a3
debugging blob load/store bug.
matthewhammer Mar 24, 2023
21cd5f5
avoid from-val to-val round-trip conversion in region0_load_blob.
matthewhammer Mar 24, 2023
ef1b7f4
fix the call conventions (on RTS side) for region0_store_blob. Thank…
matthewhammer Mar 24, 2023
00d9293
load/store blob unit test passes. Thanks Claudio!
matthewhammer Mar 24, 2023
338ce6c
copy fix to region_ API
matthewhammer Mar 25, 2023
7624356
starting test for isolation.
matthewhammer Mar 27, 2023
ecf89ff
plan cases for multi-region reads and writes.
matthewhammer Mar 27, 2023
470aedb
do loads block by block.
matthewhammer Mar 27, 2023
7c5d389
simple test for isolated regions.
matthewhammer Mar 27, 2023
6b04983
finish isolation test, which passes.
matthewhammer Mar 27, 2023
3effd5c
nits, and verify with println that we are doing the general case for …
matthewhammer Mar 27, 2023
8c02b4f
add flag for region new manager system.
matthewhammer Mar 28, 2023
cd1db26
reserving regions 2-15 for future use by Motoko.
matthewhammer Mar 28, 2023
db5a640
unit test for regions' serialization/deserialization as stable vars.
matthewhammer Mar 28, 2023
5402e4c
nit.
matthewhammer Mar 28, 2023
93c1b99
reading and making notes.
matthewhammer Mar 28, 2023
17a4e72
some attempt to start serializing the Region type.
matthewhammer Mar 28, 2023
b1b2c82
writing more cases, but unsure. same result.
matthewhammer Mar 28, 2023
e7bbf91
Update src/codegen/compile.ml
matthewhammer Mar 29, 2023
a3d25fd
WIP: region serialization
crusso Mar 29, 2023
9dde00f
no extra byte in size comp
crusso Mar 29, 2023
6d0bc06
WIP (still broken)
crusso Mar 29, 2023
7df427c
cleanup
crusso Mar 29, 2023
7409286
Merge branch 'claudio/msm' into multiple-stable-memory
matthewhammer Mar 30, 2023
a5b8605
revise doc, using new example (two regions for a random-access log).
matthewhammer Apr 3, 2023
b569f9a
WIP: region serialization (#3906)
crusso Apr 4, 2023
fcbf51a
reserve per-region space for GC meta data.
matthewhammer Apr 4, 2023
c99e09a
remove region0 from public API. Add comment explaining.
matthewhammer Apr 4, 2023
53232e3
fix tests for removed prim.
matthewhammer Apr 4, 2023
0057d44
simple actor that fills up stable memory in a predictable way.
matthewhammer Apr 7, 2023
26add78
simple actor that fills up stable memory in a predictable way. Wasm.
matthewhammer Apr 7, 2023
5e2bb2a
WIP
matthewhammer Apr 14, 2023
a8c69e5
Update doc/md/stablememory.md
matthewhammer Apr 14, 2023
8cc2826
Update doc/md/stablememory.md
matthewhammer Apr 14, 2023
b36bc66
Update doc/md/stablememory.md
matthewhammer Apr 14, 2023
5ec3952
Update doc/md/stablememory.md
matthewhammer Apr 14, 2023
dc4aea5
Update doc/md/stablememory.md
matthewhammer Apr 14, 2023
04375f1
Update doc/md/stablememory.md
matthewhammer Apr 14, 2023
3dc7fca
Update doc/md/examples/StableLog.mo
matthewhammer Apr 14, 2023
93a088b
Update doc/md/examples/StableLog.mo
matthewhammer Apr 14, 2023
d1cc1b7
Update doc/md/examples/StableLog.mo
matthewhammer Apr 14, 2023
2861f82
Update doc/md/examples/StableLog.mo
matthewhammer Apr 14, 2023
930425d
Update doc/md/examples/StableLog.mo
matthewhammer Apr 14, 2023
c29baac
attempt to merge conflict in default.nix
matthewhammer Apr 14, 2023
fa17241
Merge branch 'multiple-stable-memory' of github.com:dfinity/motoko in…
matthewhammer Apr 14, 2023
e165d36
update version number in StableMem, in compile.ml, for region system …
matthewhammer Apr 14, 2023
c4e251d
migration design.
matthewhammer Apr 14, 2023
1540993
nit.
matthewhammer Apr 14, 2023
284e2e6
nit.
matthewhammer Apr 14, 2023
bad61c5
attempt at corrections for accuracy.
matthewhammer Apr 15, 2023
58b6b56
current thoughts about migration path.
matthewhammer May 15, 2023
b7786a3
reflect discussion with Claudio.
matthewhammer May 16, 2023
08a9b11
nits and more details.
matthewhammer May 16, 2023
6a65db2
nits and more details.
matthewhammer May 16, 2023
87d92a7
nit.
matthewhammer May 16, 2023
489b33a
test commit.
matthewhammer May 16, 2023
054b018
Merge branch 'multiple-stable-memory' of github.com:dfinity/motoko in…
matthewhammer May 16, 2023
028e1a8
try to use default.nix from master.
matthewhammer May 16, 2023
b3f5aa5
trying to start using moc flag for regions. baby steps.
matthewhammer May 24, 2023
960b853
use the flag, and restore old stable mem code, case by case...
matthewhammer May 24, 2023
5cdbac5
use flag in every case.
matthewhammer May 24, 2023
7d0d33d
add flag to region tests.
matthewhammer May 24, 2023
fe210a5
restore flags
matthewhammer Jun 6, 2023
4c33508
test helper (temp file) and work log (another temp file).
matthewhammer Jun 14, 2023
a4a28a9
remove dead end.
matthewhammer Jun 14, 2023
eb29dd8
Update src/codegen/compile.ml
matthewhammer Jun 14, 2023
3b9d80a
Update rts/motoko-rts/src/region.rs
matthewhammer Jun 14, 2023
5ddae06
remove inaccurate comment.
matthewhammer Jun 14, 2023
6bccf80
Merge branch 'multiple-stable-memory' of github.com:dfinity/motoko in…
matthewhammer Jun 14, 2023
457c41a
update doc of RegionObject layout
matthewhammer Jun 14, 2023
0db11ea
re-run script on more tests.
matthewhammer Jun 15, 2023
56915e5
0 vs -128
matthewhammer Jun 15, 2023
cbf85bf
fix some OCaml precedence issues.
matthewhammer Jun 15, 2023
c8ac583
revert test.
matthewhammer Jun 15, 2023
41bd43a
restore files with success and failures
matthewhammer Jun 15, 2023
1ff83a0
progress.
matthewhammer Jun 20, 2023
8d87f8a
update default.nix to match master.
matthewhammer Jun 20, 2023
a249067
Update stable region PR to latest `master` branch. (#4057)
matthewhammer Jun 21, 2023
3f92146
Merge branch 'master' into multiple-stable-memory
matthewhammer Jun 21, 2023
c8a53fa
fix merge.
matthewhammer Jun 21, 2023
b24f65e
fix merge, again.
matthewhammer Jun 21, 2023
9af4831
test outcomes.
matthewhammer Jun 21, 2023
2362675
introduce max_pages arg for region growth.
matthewhammer Jun 22, 2023
6268fb6
custom max tests work now.
matthewhammer Jun 22, 2023
b593986
flag affects version number.
matthewhammer Jun 22, 2023
850720c
remove some old comments.
matthewhammer Jun 22, 2023
7da523c
fix.
matthewhammer Jun 22, 2023
724d631
update logs.
matthewhammer Jun 22, 2023
1d07ce3
Merge branch 'master' into multiple-stable-memory
matthewhammer Jun 22, 2023
7a5767c
working towards a test harness for migration logic. Adapted another d…
matthewhammer Jun 22, 2023
912ecfc
test actors are in place. test passes for the wrong reason.
matthewhammer Jun 22, 2023
5ae1bf2
nit: remove names.
matthewhammer Jun 22, 2023
715da4b
verify that moc_flags are ignored in drun scripts.
matthewhammer Jun 22, 2023
8c79714
Merge branch 'master' into multiple-stable-memory
matthewhammer Jun 22, 2023
0ba1746
support flags in .drun sources files
crusso Jun 22, 2023
5fe7449
removed old temp API.
matthewhammer Jun 22, 2023
90748e3
region_migration triggered at the right time.
matthewhammer Jun 22, 2023
01d6778
cargo fmt
matthewhammer Jun 22, 2023
55d31da
move logic from init into migration, where we have version numbers.
matthewhammer Jun 23, 2023
8112224
nit.
matthewhammer Jun 23, 2023
305138e
refactor.
matthewhammer Jun 23, 2023
1f2db85
draft of migration logic.
matthewhammer Jun 23, 2023
93392a4
debugging migration logic.
matthewhammer Jun 23, 2023
5bb617f
debugging. cargo fmt.
matthewhammer Jun 23, 2023
93aa66d
remove temp debug prints from test output.
matthewhammer Jun 23, 2023
c40d99e
progress.
matthewhammer Jun 23, 2023
0030b16
fix so migration logic works.
matthewhammer Jun 24, 2023
44a513b
migration test passes. update ok output.
matthewhammer Jun 24, 2023
a7c7a3a
fix tests.
matthewhammer Jun 24, 2023
d408cd9
Merge branch 'master' into multiple-stable-memory
matthewhammer Jun 24, 2023
9ffe763
test: allocate regions on each upgrade.
matthewhammer Jun 25, 2023
74a3ba7
fix rts tests.
matthewhammer Jun 26, 2023
9eb91af
Merge branch 'master' into multiple-stable-memory
matthewhammer Jun 26, 2023
d4f674a
fix test
matthewhammer Jun 26, 2023
e64b2a5
Merge branch 'multiple-stable-memory' of github.com:dfinity/motoko in…
matthewhammer Jun 26, 2023
1c617e9
conditional compilation
matthewhammer Jun 26, 2023
0aa76b0
fix build
matthewhammer Jun 26, 2023
d68c4e5
compiler provides stable64 indirection for Wasi target.
matthewhammer Jun 26, 2023
88bc18d
fix build.
matthewhammer Jun 26, 2023
f1051f4
Merge branch 'master' into multiple-stable-memory
matthewhammer Jun 27, 2023
7c9bde3
adjust test.
matthewhammer Jun 27, 2023
a35cf4a
Merge branch 'multiple-stable-memory' of github.com:dfinity/motoko in…
matthewhammer Jun 27, 2023
c17075f
experiment: multiple-stable-memory assertion fixes (#4077)
crusso Jun 27, 2023
0462528
drop allocation of region1, never given as root to any GC. (#4079)
matthewhammer Jun 27, 2023
09d8160
accept incorrect behavior for aliasing region0 as what we expect.
matthewhammer Jun 27, 2023
02028a1
fix underflow, and document the other '- 1' as safe. (#4084)
matthewhammer Jun 28, 2023
7b346fa
Merge branch 'master' into multiple-stable-memory
matthewhammer Jun 28, 2023
862ce00
fix comparison bug. (#4085)
matthewhammer Jun 28, 2023
bd783bf
call header.init_forward on newly-allocated region objects.
matthewhammer Jun 28, 2023
0a71922
integration of region0 and incremental GC root set.
matthewhammer Jun 28, 2023
e99e1b0
fix tests wrt region0_ptr_loc.
matthewhammer Jun 28, 2023
64c8656
fix build, 'cargo build --target=wasm32-wasi --features incremental_gc'
matthewhammer Jun 28, 2023
0964f06
GC Adjustments for Stable Regions (#4087)
luc-blaeser Jun 29, 2023
6bc8fa5
Merge branch 'master' into multiple-stable-memory
matthewhammer Jun 30, 2023
cdd7b01
update test output
crusso Jul 6, 2023
ad4b429
Update rts/motoko-rts/src/region.rs
matthewhammer Jul 10, 2023
d6c21b8
Update design/StableRegions.md
matthewhammer Jul 10, 2023
07d0e10
Update rts/motoko-rts/src/ic0_stable.rs
matthewhammer Jul 10, 2023
77fd927
Update rts/motoko-rts/src/region.rs
matthewhammer Jul 10, 2023
2f53218
Update rts/motoko-rts/src/ic0_stable.rs
matthewhammer Jul 10, 2023
1e87138
Update rts/motoko-rts/src/region0.rs
matthewhammer Jul 10, 2023
ebb4493
Update rts/motoko-rts/src/region.rs
matthewhammer Jul 10, 2023
a985492
Apply suggestions from code review
matthewhammer Jul 10, 2023
9298f03
Apply suggestions from code review
matthewhammer Jul 10, 2023
32e63d1
restore original StableLog.mo in doc/examples
matthewhammer Jul 10, 2023
b59e796
restore original stablememory.md
matthewhammer Jul 10, 2023
751c997
clean up.
matthewhammer Jul 10, 2023
fd79ee8
restore docs for stable-regions, as a separate file.
matthewhammer Jul 10, 2023
6eb3368
progress.
matthewhammer Jul 13, 2023
52d8e5f
fix typo.
matthewhammer Jul 13, 2023
eb61f9c
Update rts/motoko-rts/src/region.rs
matthewhammer Jul 13, 2023
5d9f31d
Update rts/motoko-rts/src/region.rs
matthewhammer Jul 13, 2023
c566c4d
Update rts/motoko-rts/src/region.rs
matthewhammer Jul 13, 2023
ca52f74
Update rts/motoko-rts/src/region.rs
matthewhammer Jul 13, 2023
86e1b2f
Update rts/motoko-rts/src/region.rs
matthewhammer Jul 13, 2023
3cde02e
Update rts/motoko-rts/src/region.rs
matthewhammer Jul 13, 2023
7117c42
Update rts/motoko-rts/src/region.rs
matthewhammer Jul 13, 2023
d187237
trigger CI
chenyan-dfinity Jul 14, 2023
c59debc
merge with master
crusso Jul 17, 2023
26bd114
fix Rust
crusso Jul 17, 2023
15d4df3
Merge branch 'master' into multiple-stable-memory
crusso Jul 18, 2023
916f7c5
fix hash, as per error in CI.
matthewhammer Jul 18, 2023
caca9a3
accept bench changes
matthewhammer Jul 18, 2023
28f09b2
accept bench changes
matthewhammer Jul 18, 2023
2d2d84b
try to build a Darwin binary.
matthewhammer Jul 18, 2023
33a0d10
try again.
matthewhammer Jul 18, 2023
cae6038
refactor artifact build
chenyan-dfinity Jul 18, 2023
bfe2a28
fix
chenyan-dfinity Jul 18, 2023
45f5c8b
bugfix: define regions to be stable, but not shared, adding testcase …
crusso Jul 25, 2023
3a7dbfe
failing test repro
crusso Jul 25, 2023
b18e1e3
use StableMem as underlying page allocator, remove region_set/get_mem…
crusso Jul 24, 2023
0a2ea99
rust format
crusso Jul 24, 2023
98ad265
remove unused globals
crusso Jul 24, 2023
63f8f93
update bench output
crusso Jul 24, 2023
306c0c7
experiment: refactor enforcement of --max-stable-pages (#4145)
crusso Jul 25, 2023
03e5e92
repro for overflowing bounds check (#4148)
crusso Jul 25, 2023
34f18e6
Merge branch 'master' into multiple-stable-memory
matthewhammer Jul 25, 2023
222f6cb
Update design/StableRegions.md
crusso Jul 26, 2023
a0f6338
Update design/StableRegions.md
crusso Jul 26, 2023
2443bc5
Apply suggestions from code review
crusso Jul 26, 2023
e4731ad
fixes after accepting suggestions
crusso Jul 26, 2023
2c9348c
detect overflows; still need to reproduce error txt
crusso Jul 26, 2023
39a30ed
harmonize stablemem and region range checks, fixing bug in stablemem …
crusso Jul 27, 2023
c4afb05
update bench data
crusso Jul 27, 2023
f05e744
refactor region_recover
crusso Jul 28, 2023
d13a799
reduce allocation in stable-region-migration.drun
crusso Jul 28, 2023
317840a
early return from region_recover
crusso Jul 28, 2023
9a66d4e
refactor tests as queries to avoid disk pressure
crusso Jul 28, 2023
3b9e651
failing test for region.new trap on overflow
crusso Jul 28, 2023
a31362e
trap on out of region failure in new_region. Add test
crusso Jul 28, 2023
1102818
add asserts (commented out for imminent removal of ops used)
crusso Jul 28, 2023
f38d90f
benchmark stablemem vs regions
crusso Jul 28, 2023
f4744cf
bench *-mem.mo: remove dep on test/run-drun
crusso Jul 28, 2023
b8642de
check for grow failure
crusso Jul 29, 2023
ea1f7a7
update bench numbers
crusso Jul 29, 2023
ce3d517
add magic header MOREGION and version to layout; minor fixes
crusso Jul 29, 2023
cc43523
repro for failure to write big blobs due limitations on Rust slices (…
crusso Jul 29, 2023
5caba85
copy large blobs in halves to avoid limitations of slices; fix test o…
crusso Jul 29, 2023
a70a9c3
update bench results
crusso Jul 29, 2023
271e78d
add region/stable-mem stats to show overhead
crusso Jul 31, 2023
4810c88
* add rts_stable_mem_size and rts_logical_stable_mem_size (in pages)
crusso Jul 31, 2023
3bc522d
make regionId return Nat, not Nat32 (for extensibility/GC); update te…
crusso Aug 1, 2023
6eff1ce
Merge branch 'master' into multiple-stable-memory
crusso Aug 2, 2023
50e30d5
re-order TAG_REGION to simplify asserts
crusso Aug 10, 2023
7aa5874
reorder Region tags in compile.ml; zap trailing whitespace
crusso Aug 10, 2023
6556e17
improve Region errors
crusso Aug 10, 2023
3a3374c
implement two missing overflow checks
crusso Aug 10, 2023
91a01b2
Merge branch 'master' into multiple-stable-memory
crusso Aug 10, 2023
da55cca
widen metadata for block allocations to 32 bits (so we can count up t…
crusso Aug 10, 2023
a3a370d
Merge branch 'multiple-stable-memory' of github.com:dfinity/motoko in…
crusso Aug 10, 2023
7eed176
update perf numbers
crusso Aug 10, 2023
d94fd70
add asserts on blob reads
crusso Aug 11, 2023
2be6b81
use new bytes_of<T> to refer to memtadata sizes
crusso Aug 11, 2023
c754dd3
use memzero_bytes and memcpy_bytes
crusso Aug 11, 2023
38c13cc
add const LAST_RESERVED_REGION_ID; refactor region_reserve_id_span
crusso Aug 11, 2023
f765f71
add assertions preventing overflow
crusso Aug 11, 2023
3a62122
optimize vector access using single u16 read/write
crusso Aug 11, 2023
052c808
restore deleted bench results
crusso Aug 11, 2023
2fd28e4
add missing barrier
crusso Aug 11, 2023
11c7fc0
refactor ic0_stable.rs to stable_mem.rs, distinguishing ic0 from moc …
crusso Aug 12, 2023
94bf29e
flatten stable_mem::util into stable_mem
crusso Aug 12, 2023
ee79edf
Merge branch 'master' into multiple-stable-memory
crusso Aug 12, 2023
da0e23f
experiment: lazy migration for stable memory for regions (#4171)
crusso Aug 16, 2023
fef65b2
regions: add missing write-barrier plus refactoring (#4172)
crusso Aug 18, 2023
0c2a7ae
regions: refactor for 64-bit region ids, dropping region table (#4176)
crusso Aug 23, 2023
365d1b8
Merge branch 'master' into multiple-stable-memory
crusso Aug 29, 2023
682c592
regions: reduce metadata overhead of code using Regions before ESM fr…
crusso Sep 5, 2023
3f76b76
experiment: increase EXTRA_BATCHES arg to drun (#4186)
crusso Sep 5, 2023
c427556
documentation and changelog for region types (#4185)
crusso Sep 5, 2023
4f8c721
Merge remote-tracking branch 'origin/master' into multiple-stable-memory
crusso Sep 5, 2023
2d9a913
update doc; add Region.md; make StableMultiLog.mo checkable
crusso Sep 5, 2023
b856317
doc tweaks
crusso Sep 5, 2023
1a5c233
document --stable-regions flag
crusso Sep 5, 2023
0300613
describe type Region and new; refactor example; rename self to state
crusso Sep 5, 2023
27a8adf
Merge branch 'master' into multiple-stable-memory
crusso Sep 5, 2023
7765630
update design doc
crusso Sep 6, 2023
f5387d0
restore stable-region-blob.mo; extract stable-region-misc.mo
crusso Sep 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 224 additions & 0 deletions design/StableRegions-20230209.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
# Stable Region Allocator Design
matthewhammer marked this conversation as resolved.
Show resolved Hide resolved

- 20230209
- Matthew Hammer

## Purpose of this document

This document gives a design for a new “stable region” allocator for Motoko’s runtime system, and accompanying `base` library.

The role of this feature is summarized in the next section,
followed by design considerations and details.


## Role for "stable regions" in Motoko

The current stable memory module in `base` has been "experimental" for a long time, and requires a more composable API to graduate from this status.

Stable regions address the problem that today's `ExperimentalStableMemory` module only provides a single, monolithic memory that makes it unsuitable for directly building composable software parts.

Stable regions permit a new API that supports composable use cases.

Stable regions also bring Motoko closer to parity with Rust canister development support today, by giving a run-time-system-based analog of a special Rust library for stable data structures that allocates “pages” for them from stable memory in separate memory regions.
matthewhammer marked this conversation as resolved.
Show resolved Hide resolved


## Design space

The design space for the page allocator is defined by at least two
tensions:

1. fully-stable representation of allocator meta data **versus** fast load/store operations.

2. Total scaling capacity **versus** minimum footprint for meta data.


**Tension 1** is introduced because we want to avoid relying on the Motoko heap as the "ground truth" about the allocator's state. If this heap is lost, as it is during an upgrade, then a developer may still want to recover all of the regions' data and meta data.
matthewhammer marked this conversation as resolved.
Show resolved Hide resolved

On the other hand, during ordinary canister execution, ~we *do* want to rely on the heap (not stable memory) for meta data to avoid its higher access costs for load and store operations, and thus we need meta data in two places, both heap and stable memory.~

Tension 1 is resolved by ~storing certain meta data twice, just as with the Rust implementation that serves as our basis.~ storing the data only in stable memory, and relying on those accesses becoming faster in the near future. (looking into the near-future roadmap for canister stable memory, it seems this is very likely.)

Compared with the Rust version, we store enough extra meta data to permit:

- Regions whose page blocks are in arbitrary order, not
matthewhammer marked this conversation as resolved.
Show resolved Hide resolved
necessarily in order of smallest to highest address.

- 32767 Regions max (instead of 255 Regions max).

We want to permit arbitrary page block orders to make a smooth
transition to region reclamation and re-allocation in the near
future, with potential integration into the Motoko GC. The
extra complexity is modest, and seems "worth" the cost.

We change the maximum region limit because 255 may be too small in
some extreme cases.

We address the question of whether the new limit of 32k regions is
"enough" in the Q&A section (it is, for all practical purposes)


**Tension 2** is introduced because we want a design that will continue
to work even when canisters can store more stable data than today (32GB).
matthewhammer marked this conversation as resolved.
Show resolved Hide resolved

Tension 2 is resolved by making prudent representation choices.

The representations we choose for regions and region identifiers
permit a scaling to 256GB of stable data while still permitting meta
data to be repeated in both stable and non-stable arenas. These are
the same limits imposed by the Rust implementation, for the same
reasons. See Q&A for more discussion.



## Questions and answers

### Q: What determines the 8MB non-empty region minimum?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 8MB default block size turned out to be a footgun for OpenChat that has thousands of canisters. The developers were not aware that stable-structures are allocating at least 8MB even if the actual usage is a few KB.

It might become an issue for Motoko canisters too. Ideally, the developer provides this number themselves such that they are aware of the implications. How difficult it is to make this a parameter that's configurable at runtime?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Rust we do expose an API that allows users to change the region/bucket size from 8MiB to something else. It does bring additional complexity to developers though as they now need to understand that regions are pre-allocated with some minimum size, but maybe that's an unavoidable detail.

On a related note, I'm currently benchmarking smaller region sizes in the Rust implementation to better understand the performance trade-offs. I'll share those results once I have them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ulan and @ielashi

I liked how the 8MB minimum simplified some other decisions for the implementor, but I also wondered how to guide people to use these the right way without running up a budget of unused scratch space in their apps.

Since we are trying to "catch up" to the Rust stable memory manager at the moment, and not surpass its features, I'll stick with the current 8MB minimum for now. But I'm very curious to hear about the future test results that @ielashi may have about other options.

I'm also curious about a more layered approach, where initially-small regions can use a more fine-grained setting, and then use coarser blocks as they grow. That seems potentially too complex, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ulan asks

How difficult it is to make this a parameter that's configurable at runtime?

That's a good question. At "runtime" still means fixed at some point, right?

When would that parameter be given during the compilation and deployment phases, I wonder? Would it be a parameter to the actor, given when it's first installed?


This size comes from wanting to grow a region by more than one page at
a time (in terms of the way that the canister interacts with the
system API, at least). Rather than actually grow by a single page,
the implementation grows by a "page block" (8MB) at a time.

This choice means that there are 128 pages per page block, and that
the maximum number of regions and region blocks are each relatively
small (32k each). Consequently, they can each be identified with a
2-byte identifier, and we can pre-allocate tables to store certain
matthewhammer marked this conversation as resolved.
Show resolved Hide resolved
relations about them, which is critical.

### Q: Are 32767 regions enough?
matthewhammer marked this conversation as resolved.
Show resolved Hide resolved

A: Permitting more than 32k regions may seem theoretically
interesting, but is not practical given other parameters today that
control the minimal footprint of a region (8MB) and dictate the
maximum size of stable memory for a canister today (32GB). With 32k
regions at 8MB each, well over the maximum stable memory size is used
(256GB compared to 32GB, the limit today)

### Q: When is stable memory becoming less costly?

Spring 2023.

### Q: How does the cheaper stable memory access cost compare with ordinary heap memory access cost?

2-3x slower than ordinary heap memory.


## Design details

### API

Internal region allocator operations:

- `initialize` -- called by the RTS, not by the Motoko developer.

User-facing region allocator operations:

- `region_new` -- create a dynamic region.
- `region_grow` -- grow region by a specified number of pages.
- `region_load` -- read some data from the region.
- `region_store` -- store some data into the region.

And a special operation, for testing our design for future GC integration (bonus):

- `region_release` -- release region and reuse region's page blocks.

The `_release` operation is *not* part of the user-facing API nor part of the MVP,
but supporting it is important because it means we can transition quickly to an integration
with the ambient Motoko GC if we can support it.

Another special operation, for disaster recovery:

- `rebuild` -- not needed unless we need to recreate all Region objects from their stable-memory counterparts.


## Internal footprint

The state of the allocator is stored in a combination of:

- stable memory fields and tables and
- stable heap memory, in the form of objects of type `Region`.

The stable memory state is sufficient to reconstitute the stable heap objects
(`rebuild` operation, described in a subsection below).

That means that even if the stable parts of the heap are lost, the
stable memory state can fully describe the region objects that will be rebuilt when it succeeds.

### stable memory fields

- total allocated blocks, `Nat16`, max value is `32768`.
- total allocated regions, `Nat16`, max value is `32767` (one region is reserved for "no region").
matthewhammer marked this conversation as resolved.
Show resolved Hide resolved
- The `block-region` table (fixed size, about 131kb).
- The `region` table (fixed size, about 262kb).

### representation of values of type `Region`

- A singleton, heap-allocated object with mutable fields.
- While being heap-allocated, the object is also `stable` (can be stored in a `stable var`, etc).
- `RegionObject { size_in_pages: Nat64; id: Nat16; vec_capacity: Nat16; vec_ptr: Nat32 }`
- Field `size_in_pages` gives the number of pages allocated to the Region.
- Field `id` gives the Region's numerical id as an index into the `region` table.
- Fields `vec_capacity` and `vec_ptr` work with `size_in_pages`
to represent a growable vector that we call the region's **"access
vector"** (because "blocks vector" sounds a bit strange, and its
used to support O(1) access operations):
- the access vector's address is held in `vec_ptr` and it has `vec_capacity` slots.
- the first `size_in_pages / 128` slots of `vec_ptr` contain a valid page block ID for the region.
matthewhammer marked this conversation as resolved.
Show resolved Hide resolved
- the access vector doubles when it grows.
- no region has more than 32k page blocks, so a `Nat16` suffices for `capacity`,
- during an upgrade, the access vectors get serialized and deserialized as data `Blobs` (as if no pointers are inside each).


### region-blocks relation

The `region-blocks` relation is not materialized into a table in stable memory (doing so with a pre-allocated table would be prohibitively large).

Instead, this relation is represented in two ways at the same time:
1. by the set of heap-allocated region objects, and their access vectors. The access vectors provide O(1) store and load support.
2. by the `block-region` table and the `region` table, which together are sufficient to recreate all of the heap-allocated region objects.

In ordinary operation, the second feature is not required. In the event of an upgrade failure, however, it could be vital (See `rebuild`).

### block-region table

- purpose:
- relate a block ID ("page block ID") to its region (if any) and its position in that region (see `rebuild`).
matthewhammer marked this conversation as resolved.
Show resolved Hide resolved

- NB: The organization of this table is not useful for efficient
access calculations of load or store (they require a linear
search that would be prohibitively slow). OTOH, this
table is suitable to do a "batch" rebuild of the dynamic heap-allocated vectors
in that table, if need be (see `rebuild`).

- 32768 entries (statically sized).
- 4 bytes per entry.
- entry type = `BlockRegion { region : Nat16, position : Nat16 }`
- the location of each entry gives its corresponding block ID.

### region table

- purpose:
- relate a region ID to its size in pages.

- NB: This table exists as a "stable backup" of the per-region fields
that exist in the set of region objects, except for the access vectors' elements (see `block-region` table for their data).

- 32768 entries (statically sized).
- 8 bytes per entry.
- entry type = `RegionBlocks { size_in_pages: Nat64; }`
matthewhammer marked this conversation as resolved.
Show resolved Hide resolved
- the location of each entry in the table gives its corresponding region ID.
- the `size_in_pages` field measures the size of the region in terms of _pages_, not blocks.


### Overview of `rebuild`

When upgrades work as expected, stable `Regions` are serialized and deserialized just like other stable data.

For disaster recovery, we can **also** rebuild all of the region objects from data in stable memory.

We use the `block-region` and `region` tables in stable memory to rebuild the regions' objects:

matthewhammer marked this conversation as resolved.
Show resolved Hide resolved
- The `region` table gives a size for each region, saying how large each vector for each region needs to be.
- The `block-region` table gives a relative position and region ID for each block ID.

Once each regions' vectors have been allocated, the block-region table says how to fill them, one entry at a time.
matthewhammer marked this conversation as resolved.
Show resolved Hide resolved
23 changes: 23 additions & 0 deletions design/StableRegions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Stable Region API

See StableMemory.md for context of the current experimental API.

This document aims to specify the API and memory representations for a generalization
of this API that permits multiple _regions_ of stable memory, where each can be
grown independently.

The **region manager** is the state and logic to support this generalization.

## Definitions and constants

- a **page** is 65536 bytes.
- a **page block** is a contiguous sequence of 128 pages (~8MB).
- a **page block index** is a 16 bit, index-based identifier for a page block.
- a **region** is a sequence of (generally non-contiguous) **page blocks**.
- the maximum number of page blocks is 32768.
- the maximum amount of stable memory for all regions is 256GB.




###### TO DO -- finish this document.
1 change: 1 addition & 0 deletions rts/motoko-rts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ mod tommath_bindings;
pub mod types;
pub mod utf8;
mod visitor;
pub mod region;

use types::Bytes;

Expand Down
30 changes: 30 additions & 0 deletions rts/motoko-rts/src/region.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use crate::memory::{Memory};
use crate::rts_trap_with;
use crate::types::{Value};

use motoko_rts_macros::ic_mem_fn;

#[ic_mem_fn]
pub unsafe fn region_new<M: Memory>(_mem: &mut M) -> Value {
rts_trap_with("TODO region_new");
}

#[ic_mem_fn]
pub unsafe fn region_size<M: Memory>(_mem: &mut M, _r: Value) -> Value {
rts_trap_with("TODO region_grow");
}

#[ic_mem_fn]
pub unsafe fn region_grow<M: Memory>(_mem: &mut M, _r: Value, _new_pages: Value) -> Value {
rts_trap_with("TODO region_grow");
}

#[ic_mem_fn]
pub unsafe fn region_load_blob<M: Memory>(_mem: &mut M, _r: Value, _start: Value, _len: Value) -> Value {
rts_trap_with("TODO region_load_blob");
}

#[ic_mem_fn]
pub unsafe fn region_store_blob<M: Memory>(_mem: &mut M, _r: Value, _start: Value, _blob: Value) {
rts_trap_with("TODO region_store_blob");
}
2 changes: 2 additions & 0 deletions rts/motoko-rts/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,8 @@ pub const TAG_CONCAT: Tag = 25;
pub const TAG_NULL: Tag = 27;
pub const TAG_ONE_WORD_FILLER: Tag = 29;
pub const TAG_FREE_SPACE: Tag = 31;
pub const TAG_REGION_OBJ: Tag = 33;
pub const TAG_REGION_VEC: Tag = 35;
matthewhammer marked this conversation as resolved.
Show resolved Hide resolved

// Special value to visit only a range of array fields.
// This and all values above it are reserved and mean
Expand Down
53 changes: 53 additions & 0 deletions src/codegen/compile.ml
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,11 @@ module RTS = struct
E.add_func_import env "rts" "text_singleton" [I32Type] [I32Type];
E.add_func_import env "rts" "text_size" [I32Type] [I32Type];
E.add_func_import env "rts" "text_to_buf" [I32Type; I32Type] [];
E.add_func_import env "rts" "region_new" [] [I32Type];
E.add_func_import env "rts" "region_size" [I32Type] [I32Type];
E.add_func_import env "rts" "region_grow" [I32Type; I32Type] [I32Type];
E.add_func_import env "rts" "region_load_blob" [I32Type; I32Type; I32Type] [I32Type];
E.add_func_import env "rts" "region_store_blob" [I32Type; I32Type; I32Type] [];
E.add_func_import env "rts" "blob_of_principal" [I32Type] [I32Type];
E.add_func_import env "rts" "principal_of_blob" [I32Type] [I32Type];
E.add_func_import env "rts" "compute_crc32" [I32Type] [I32Type];
Expand Down Expand Up @@ -3374,6 +3379,20 @@ module Blob = struct

end (* Blob *)

module Region = struct
(*
See rts/motoko-rts/src/region.rs
*)
let new_ env =
E.call_import env "rts" "region_new"
let size env =
E.call_import env "rts" "region_size"
let grow env =
E.call_import env "rts" "region_grow"
let load_blob env = E.call_import env "rts" "region_load_blob"
let store_blob env = E.call_import env "rts" "region_store_blob"
end

module Text = struct
(*
Most of the heavy lifting around text values is in rts/motoko-rts/src/text.rs
Expand Down Expand Up @@ -4935,6 +4954,7 @@ module MakeSerialization (Strm : Stream) = struct
| Any -> Some 16l
| Non -> Some 17l
| Prim Principal -> Some 24l
| Prim Region -> Some 25l
| _ -> None

(* some constants, also see rts/idl.c *)
Expand Down Expand Up @@ -9185,6 +9205,39 @@ and compile_prim_invocation (env : E.t) ae p es at =
SR.Vanilla,
GC.get_collector_instructions env ^^ BigNum.from_word64 env

(* Regions *)

| OtherPrim "regionNew", [] ->
SR.Vanilla,
Region.new_ env

| OtherPrim ("regionGrow"), [e0; e1] ->
SR.UnboxedWord64,
compile_exp_as env ae SR.Vanilla e0 ^^
compile_exp_as env ae SR.UnboxedWord64 e1 ^^
Region.grow env

| OtherPrim "regionSize", [e0] ->
SR.UnboxedWord32,
compile_exp_as env ae SR.Vanilla e0 ^^
Region.size env

| OtherPrim ("regionLoadBlob"), [e0; e1; e2] ->
SR.Vanilla,
compile_exp_as env ae SR.Vanilla e0 ^^
compile_exp_as env ae SR.UnboxedWord64 e1 ^^
compile_exp_as env ae SR.Vanilla e2 ^^
Blob.lit env "Blob size out of bounds" ^^
BigNum.to_word32_with env ^^
Region.load_blob env

| OtherPrim ("regionStoreBlob"), [e0; e1; e2] ->
SR.unit,
compile_exp_as env ae SR.Vanilla e0 ^^
compile_exp_as env ae SR.UnboxedWord64 e1 ^^
compile_exp_as env ae SR.Vanilla e2 ^^
Region.store_blob env

(* Other prims, unary *)

| OtherPrim ("global_timer_set"), [e] ->
Expand Down
1 change: 1 addition & 0 deletions src/mo_frontend/coverage.ml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ let pick_val vs = function
| T.Blob
| T.Error
| T.Principal
| T.Region
| T.Float -> Any

let rec expand_notval t n vs : desc list =
Expand Down
1 change: 1 addition & 0 deletions src/mo_idl/mo_to_idl.ml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ let prim p =
| Text -> I.PrimT I.Text
| Blob -> I.BlobT
| Principal -> I.PrincipalT
| Region -> assert false
| Error -> assert false

let rec typ t =
Expand Down
Loading