“经过社区的讨论,Dfinity 开放了提升 Canister 内存容量的 NNS 提案,目前,该提案已经由社区投票通过。该提案建议为容器设计一个新的系统 API,使得 Canister 可使用的内存增加,后续会有代码的正式更新。”
Canister的总体内存包含wasm run time和stable memory,对一个Canister而言, 两个都为4G。
由于wasmtime是32bit的指针地址空间,因此是4G,而stable memory需要和wasm time适配, 所以也是4G,本次提案提高的内存为Stable内存, 使一个Canister可使用的最大内存为全部子网内存, 但此次开放的API将只支持Canister最大使用8G Stable内存。
Wasm 单体的内存空间为4G, 但是由于Motoko所选用的GC(Garbage Collector 垃圾回收)算法为Copying算法[1],造成由Motoko写的Canister只能使用2G内存空间。
[1]Coping Collector Algorithm(Minor GC),该算法在WASM Canister中的应用:
1. 4G 的Canister被划分为两个区域 :from space & to space, 分别占用2G;数据写入时, 所有的数据都在from space中。
2. 在进行dfx canister install xxx(default : –all) — mode upgrade的时候, 会进行GC,即:将活动对象(正在使用的对象,可以是引用也可以是元数据)从from space移动到to space, 剩下的即内存垃圾, 接下来,删除掉from space中的数据(内存垃圾), 最后将from space 与 to space的名称互换。
P.S. Motoko现在已可指定Compacting GC算法, 这个算法不会占用一半的内存来进行GC,可以使用尽可能大的WASM内存。
这带来的劣势是:当堆内存在升级时有大量要写入stable内存的数据时,可能会将cycle消耗完,导致无法升级。
https://forum.dfinity.org/t/increased-canister-storage/6148/67
提案内容:提供一个系统API, 使每个Canister都可以使用子网所有的内存空间, 所有的副本都有自己的数据状态, 每个Canister均有自己的WASM运行时内存和Stable内存空间。
新增的System API (面向Canister):
ic0.stable64_write: (offset: i64, src: i64, size: i64) -> ()
ic0.stable64_write接口是为了WASM 64进行准备的
ic0.stable64_read: (dst: i64, offset: i64, size: i64) -> ()
ic0.stable64_size: () -> (page_count: i64)
ic0.stable64_grow: (additional_pages: i64) -> (old_page_count: i64
• 最近将提交修改IC代码的提案。
带来影响:
• 对Canister而言, Canister可以使用的内存从4G -> 8G(stable内存而非堆内存)。并且 8G是当前提案的结果, 以后会根据社区反馈会提升这个数字。
• 如果stable内存高于4G, 那么使用了升级内存API的Canister和没有使用此API的Canister交互将会出现问题,当前的方案是发生上述情况时,Canister会崩溃。
• WASM 64 :单个Canister升级为64bit的地址空间, 最大内存为2^64 Byte(16 T)• 数据迁移所牵扯的数据量会很大,会出现新的问题,比如升级时数据迁移量大等。
Motoko
Rust
• 使用IC “aaaaa-aa” Actor可以访问IC.status, 也可以返回上面说到的内存数据;
• Rust可以在非Upgrade时期, 通过cdk中提供的API操作Stable内存。
DFX & Moc
moc 中flag可选 –compacting gc 来更换coping gc, compacting gc下motoko编译的Canister将可以使用4G 的WASM 内存。
Stable内存
stable只能用于需要持久化存储的全局变量,只在upgrade的时候写入stable内存用。对于Canister, Canister的每个Replica都有自己的stable内存, 并非所有的Replica共享一份Stable内存。
1. 作用于存储的数组最好使用Blob而非[Nat8], 因为初始化数组时, 由于泛型的需要, 所有通过[X]或者Array.init初始化的数组内存布局是相同的。
2. 当前比较成熟的解决方案:存储的数据直接放入stable内存中, 堆内存用来放置检索数据。
外部链接
[1] Forum Stable Memory Roadmap https://forum.dfinity.org/t/increased-canister-storage/6148/70?u=c-b-elite
原文始发于微信公众号(ICPLabs):浅谈IC Storage存储技术