Skip to content

docs: canister optimization guide#93

Merged
marc0olo merged 2 commits into
mainfrom
docs/guides-canister-management-optimization
Apr 16, 2026
Merged

docs: canister optimization guide#93
marc0olo merged 2 commits into
mainfrom
docs/guides-canister-management-optimization

Conversation

@marc0olo
Copy link
Copy Markdown
Member

Summary

  • ic-wasm shrink via icp.yaml recipe and direct CLI usage
  • Rust Cargo profile tuning: lto, opt-level, codegen-units
  • Motoko GC options: incremental GC under enhanced orthogonal persistence, legacy choices
  • WebAssembly SIMD: global and per-function enablement
  • Performance counters: instruction_counter() and call_context_instruction_counter()
  • Low Wasm memory hook: Rust (#[on_low_wasm_memory]) and Motoko (system func lowmemory)

Sync recommendation

informed by dfinity/icp-cli-recipes — recipes/rust, recipes/motoko; dfinity/cdk-rs — ic-cdk/src/api.rs

@marc0olo
Copy link
Copy Markdown
Member Author

Review: Canister Optimization

Must fix

  • codegen-units = 1 attributed to the wrong example: The page states "These settings are used by the SIMD example in dfinity/examples (rust/simd/Cargo.toml) and represent good defaults for production canisters." The actual SIMD example's Cargo.toml only contains lto = true and opt-level = 3 — there is no codegen-units = 1 in the source. Remove the attribution or correct it. The setting itself is valid advice; the false "used by the SIMD example" citation is the problem.

  • Motoko low_wasm_memory upstream example uses mo:base: The page directs readers to motoko/low_wasm_memory in dfinity/examples. That example's source (main.mo) imports mo:base/Array, mo:base/Deque, and mo:base/Buffer — all banned imports. The page's inline Motoko snippet is clean (no imports), but the pointer to the example as a "complete" reference risks sending readers to code that violates project conventions. Add a note that the upstream example uses the legacy mo:base library, or avoid linking directly to it until it is updated.

Suggestions

  • on_low_wasm_memory import path is non-idiomatic: The Rust snippet uses use ic_cdk_macros::on_low_wasm_memory;. The official ic_cdk documentation (ic-cdk/src/macros.rs doc comment example) uses use ic_cdk::on_low_wasm_memory; as the canonical import — on_low_wasm_memory is re-exported from ic_cdk directly. Prefer the ic_cdk path to avoid depending on the internal ic_cdk_macros crate directly. Either use ic_cdk::on_low_wasm_memory; with #[on_low_wasm_memory], or the qualified #[ic_cdk_macros::on_low_wasm_memory] (as used in the dfinity/examples source) are both fine. The current combination of importing from ic_cdk_macros then using without the crate prefix is valid but unusual.

  • instruction_counter() description slightly imprecise: The page says "instructions executed since the start of the current message execution." The upstream source describes it as "since the last entry point." Both are correct, but "since the last entry point" is more precise and aligns with IC interface spec terminology. Each await point creates a new entry point, which is why the counter resets. Consider updating to: "instructions executed since the last entry point (resets at each await)."

  • GC section: --incremental-gc in recipe example is redundant for new projects: The Motoko compiler reference confirms incremental GC is the default for both enhanced orthogonal persistence and legacy persistence. Explicitly setting args: --incremental-gc is harmless but may imply it is required. Add a note that for new projects using enhanced orthogonal persistence (the current default), no args configuration is needed — the args field only becomes relevant when selecting an alternative GC under --legacy-persistence.

  • wasm_memory_threshold trigger condition uses ambiguous "memory usage": The statement "When memory usage exceeds wasm_memory_limit - wasm_memory_threshold, the hook fires" is correct but "memory usage" is ambiguous (Wasm heap vs. stable memory). Clarify to "When Wasm heap memory in use exceeds wasm_memory_limit - wasm_memory_threshold."

Verified

  • icp canister settings update flags --wasm-memory-limit and --wasm-memory-threshold verified against .sources/icp-cli/docs/reference/cli.md lines 468-469 — correct.
  • YAML settings.wasm_memory_limit and settings.wasm_memory_threshold field names verified against .sources/icp-cli/docs/schemas/icp-yaml-schema.json and .sources/icp-cli/docs/reference/configuration.md — correct.
  • shrink: true option in Rust and Motoko recipe YAML verified against .sources/icp-cli-recipes/recipes/rust/recipe.hbs and recipes/motoko/recipe.hbs — correct.
  • instruction_counter() and call_context_instruction_counter() names and u64 return type verified against .sources/cdk-rs/ic-cdk/src/api.rs — correct.
  • #[ic_cdk_macros::on_low_wasm_memory] attribute verified against .sources/cdk-rs/ic-cdk-macros/src/lib.rs and .sources/cdk-rs/ic-cdk/src/macros.rs — correct. Also confirmed ic_cdk::on_low_wasm_memory re-export exists.
  • Rust low-memory hook with_state_mut pattern verified consistent with .sources/examples/rust/low_wasm_memory/src/low_wasm_memory_hook/src/lib.rs.
  • Motoko system func lowmemory() : async* () signature verified against .sources/examples/motoko/low_wasm_memory/src/low_wasm_memory_hook/main.mo — correct.
  • persistent actor Motoko syntax verified against Motoko compiler docs — correct.
  • Motoko GC flags and their availability constraints verified against .sources/motoko/doc/md/15-compiler-ref.md — incremental GC is default for both modes; --copying-gc, --compacting-gc, --generational-gc require --legacy-persistence — page is accurate.
  • #[target_feature(enable = "simd128")] syntax and .cargo/config.toml rustflags pattern verified against .sources/examples/rust/simd/.cargo/config.toml and mat_mat_mul/src/lib.rs — correct.
  • Cargo.toml [profile.release] with lto = true and opt-level = 3 verified against .sources/examples/rust/simd/Cargo.toml — correct; however codegen-units = 1 is NOT present (see Must fix).
  • All "Next steps" links verified: large-wasm.md exists, ../../reference/cycles-costs.md exists, lifecycle.md resolves to lifecycle.mdx (Astro resolves .md to .mdx) — all valid.
  • <\!-- Upstream: --> comment present and well-formed — CI requirement satisfied.
  • Frontmatter complete (title, description, sidebar order) — valid.
  • No dfx references — rule satisfied.
  • No mo:base imports in any inline code snippet — rule satisfied.
  • All inline snippets under 30 lines — rule satisfied.
  • Page uses .md format — correct.
  • rust/performance_counters, rust/low_wasm_memory, motoko/low_wasm_memory, rust/simd example directories all verified to exist in .sources/examples/ — correct.

- Correct Cargo.toml attribution: only lto and opt-level=3 are from
  the SIMD example; codegen-units=1 is described as additional advice
- Add note that motoko/low_wasm_memory upstream example uses mo:base;
  direct readers to inline snippet for mo:core-compatible code
- Update on_low_wasm_memory import to use canonical ic_cdk re-export
- Improve instruction_counter() description: "since the last entry
  point" is more precise than "since the start of the current message"
- Add note clarifying --incremental-gc args is redundant for new
  projects using enhanced orthogonal persistence (the current default)
- Clarify wasm_memory_threshold trigger: "Wasm heap memory in use"
  instead of ambiguous "memory usage"
@marc0olo
Copy link
Copy Markdown
Member Author

<!-- feedback-addressed -->
Feedback addressed:

Changes applied

Must fix

  • Fixed: codegen-units = 1 attribution — The page previously stated "These settings are used by the SIMD example in dfinity/examples (rust/simd/Cargo.toml)". Verified .sources/examples/rust/simd/Cargo.toml — it only contains lto = true and opt-level = 3. Updated text to: "lto = true and opt-level = 3 are used by the SIMD example. Adding codegen-units = 1 further enables cross-function optimization and is a good default for production canisters."

  • Fixed: Motoko low_wasm_memory upstream example uses mo:base — Verified .sources/examples/motoko/low_wasm_memory/src/low_wasm_memory_hook/main.mo imports mo:base/Array, mo:base/Deque, and mo:base/Buffer. Updated the reference to note that the Motoko upstream example uses the legacy mo:base library, directing readers to the inline snippet as the mo:core-compatible reference.

Suggestions applied

  • Updated on_low_wasm_memory import path — Changed from use ic_cdk_macros::on_low_wasm_memory; to use ic_cdk::on_low_wasm_memory;. Verified in .sources/cdk-rs/ic-cdk/src/macros.rs that on_low_wasm_memory is re-exported from ic_cdk directly (pub use ic_cdk_macros::on_low_wasm_memory;) and the doc comment example shows use ic_cdk::on_low_wasm_memory; as canonical.

  • Improved instruction_counter() description — Changed "instructions executed since the start of the current message execution" to "instructions executed since the last entry point. Resets at each await point (each await creates a new entry point)." Aligns with IC interface spec terminology and is more precise.

  • Added note clarifying --incremental-gc is redundant for new projects — Added a callout explaining that for new projects using enhanced orthogonal persistence (the current default), no args configuration is needed. The args field only becomes relevant when selecting an alternative GC under --legacy-persistence.

  • Clarified wasm_memory_threshold trigger condition — Changed "When memory usage exceeds wasm_memory_limit - wasm_memory_threshold" to "When Wasm heap memory in use exceeds wasm_memory_limit - wasm_memory_threshold" to eliminate ambiguity between Wasm heap and stable memory.

Items skipped

None — all feedback items were factually correct and improve the page.

@marc0olo marc0olo merged commit f74e0b1 into main Apr 16, 2026
1 check passed
@marc0olo marc0olo deleted the docs/guides-canister-management-optimization branch April 16, 2026 19:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant