Skip to content

Pre-initialized suites (build-time wizer snapshot of the case table) #25

Description

@lann

Make suite core modules pre-initialize at build time (wizer, now wasmtime wizer): an SDK-generated init function builds the internal case table — parse vectors, expand parameter grids, warm the interpreter — into guest memory without crossing any WIT boundary; snapshot; then componentize. Every fresh instance is born with the table built and all() becomes a cheap walk over prebuilt memory.

Why

  • Makes instance-per-case cheap at the source. The instance-per-case execution default (Runner execution-policy guidance #22) turns enumeration into O(N²) case-materializations across a run; pre-initialization amortizes all enumeration work to build time. Complements (does not replace) lazy case materialization, and stacks with runner-side pooling/InstancePre (snapshot removes guest work; pooling removes instantiation cost — orthogonal).
  • Stronger determinism than fresh instantiation alone: every case starts from a bit-identical warmed heap — even insecure-seed-derived state (hashmap layouts) is uniform across cases and across runs.
  • Interpreted languages get it by construction: componentize-js already wizens engine + source (StarlingMonkey), so the suites with the most expensive dynamic enumeration are already halfway there; the Rust SDK should offer the equivalent as a build step.

Constraints

  • Pure init discipline (SDK-enforced): the wizer init function may not call imports — no entropy, no clocks, no I/O during table construction (Deterministic randomness by virtualizing wasi:random #24's no-ambient-randomness posture makes this natural). Case handles are never part of the snapshot: they are minted per-instance on demand; only the guest-memory table is snapshotted.
  • Core-module level only, today. Component-level snapshotting (multiple core instances + host-side canonical ABI handle tables) does not exist; a runner-side "pre-enumerated wrapper component holding case handles" is unimplementable until it does — handles snapshotted in wrapper memory are i32s whose meaning dies with the instance, and the wrapper's all() call is an import call, which wizer init forbids. Revisit the runner-side variant if/when component-level pre-init materializes.
  • Data-section growth: baked tables enlarge the artifact; wasm-opt after snapshotting recovers some (init-only code becomes dead).

Non-goal clarification

Stable case indexes do not depend on this: deterministic all() + the lockfile already define the authoritative, cross-instance index space. What pre-initialization buys is amortized enumeration cost and heap-level reproducibility, not index stability.

Home: guest SDK build pipeline (L0), plus a line in runner-policy guidance (#22): wizened suites make instance-per-case cheap — prefer them.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions