PPysolate SpineREAD THE CODE · UNDERSTAND THE SYSTEM
课程与章节
IMPLEMENTATION / 05

可选:Prepared full-copy

先理解干净基线与全量私有副本。

本章目录
配套饭点图解 · 1 节(按需展开)

需要的背景集中在 Go 的值与内存 与 内存/ABI。这是可后读的旁支,不需要先学 Linux 虚拟内存实现。

面试准备以 PLM 为主。这里用一个短的全量复制实现交代“干净基线 → 私有 Guest”,本章只讲 full-copy,不做性能测量。另一条 Linux COW 路径 用于理解真实共享页与私有写入。第一次学习可以先跳过本章。

运行

go run ./cmd/demo -prepared-copy
# {"item": "book", "total": 42}
go run ./cmd/demo -source examples/order.py -prepared-copy -plm
# {"item": "book", "total": 47}
go run ./cmd/demo -source examples/order.py -prepared-copy -prefix
# {"item": "book", "total": 47}

-prepared-copy 只选择 Guest 的创建方式;PLM、prefix、工具注册、结果交付、Future 和取消仍是原有路径。默认不加这个参数时仍采用 fresh 初始化。

只读 prepared.go

读 prepared.go 的 full-copy 路径时,先看两个函数:

  • NewPrepared:先像 New 一样建立 runtime/编译代码;创建一个参考 Guest,运行 _initialize 和 CPython init;把此时的整份线性内存复制到 Runner 私有的 image,关闭参考 Guest。不运行用户代码,也不开放自定义 warmup 脚本。
  • newGuest:为每轮创建独立 Wasm 实例和 I/O,调用 _initialize。fresh 模式继续调用 CPython init;full-copy 模式将内存增长到基线长度,然后用 Memory.Write(0, image) 全量复制,跳过再次调用 CPython init。
NewPrepared: 参考 Guest → init 完成 → 复制为 Go image → 关闭参考 Guest
每轮运行:    新 Guest → _initialize → 全量复制 image → 普通 / PLM / prefix

为什么在 _initialize 之后复制?实例化的 data segments 和运行库初始化会写内存。先复制再初始化,可能覆盖已准备的状态。

Runner.image 从参考内存复制得到,不借用已关闭 Guest 的切片;实例的 Memory.Write 又复制一份,所以实例之间和基线之间没有可写别名。Runner.Close 释放对 image 的引用。关闭必须发生在本 Runner 的所有 Run 返回之后,与原有约定相同。

full-copy 与 COW 的区别

  • 当前 full-copy:每个实例付出整份内存复制和私有存储,借此说明基线恢复及写入隔离。
  • 真正 COW:可共享尚未修改的物理页,写入时才分离。见独立的 Linux COW 章节,不要把 full-copy 路径误称为 COW。
  • 两者都可以让程序看到私有状态,但不能据此声称内存成本或启动性能相同。本项目没有测这些指标。

Go 的 runState、Host 工具、Future 和 prefix producer 不放进 image。基线是在任何用户源码、inputs、工具调用和 prefix 接收之前捕获的;复制一个已经带请求数据的运行中 Guest 不是这里支持的操作。

范围:不是通用 Wasm checkpoint

只支持本仓库固定 Guest 在干净 init 返回后的内存基线,不捕获任意执行栈、Wasm globals/tables 或 Host 文件描述符等外部状态。新实例自己的 _initialize 和静态初始化仍然执行。

当前 artifact 的这个静止初始化边界已通过真实执行验证。若以后增加会改变这些边界的 Guest 初始化、外部资源或可变非内存状态,要重新判断能否这样复制;不为未发生的需求预建通用 snapshot 框架。

Full-copy 本身不依赖 Linux 的页映射接口。它与 Linux COW 使用同一份 Guest artifact;两者都只复用干净初始化状态。

测试在回答什么

prepared_test.go 检查基线真实存在、捕获没有调用 Host 工具,并有 7 个真实 Guest 子测试:

  1. 普通 Host 工具返回值正确。
  2. 一轮修改 builtins,下一轮看不到。
  3. 两个同时存活的实例:写一个实例的内存不改变另一个,也不改变 image。
  4. PLM 的独立工具仍能用启动信号握手证明重叠。
  5. prefix 的工具仍在源码 EOF 前启动。
  6. Python 错误不改变后续实例的基线。
  7. 中止无限循环后,下一轮仍正常。

这是功能和生命周期检查,不是复制速度/内存 benchmark。