skilly. Buy ad slot
All skills
Rust · Frontend · Nodejs / AGENT SKILL

perf

microsoft/webui
0 installs 92 GitHub stars
0

Apply speed and memory performance rules across Rust crates, the Node addon, webui-framework, and webui-router.
Speed and memory performance rules for Rust crates, the Node addon, webui-framework, and webui-router.

BEFORE YOU INSTALL

Understand the trade-offs.

SECURITY REVIEW

Not yet assessed

Review the original instructions and requested permissions before installing.

No security review is available for this catalog entry yet.

SKILL QUALITY

Not yet assessed

How clearly the skill guides your agent, how complete its workflow is, and how you can check the outcome.

No quality assessment is available for this catalog entry yet.

The full skill.

Original instructions from the publisher’s SKILL.md

# Performance

WebUI's value proposition is speed and low memory usage. Every change to core Rust crates, `@microsoft/webui-framework`, or `@microsoft/webui-router` must be evaluated through two lenses: **throughput** (how fast) and **memory** (how little).

Server memory is not cheap. Client memory is not unlimited. Every allocation that can be avoided is a win on both sides.

Use this skill when modifying any performance-sensitive code across the stack.

## Rust - speed rules

These apply to `webui-handler`, `webui-state`, `webui-expressions`, `webui-parser`, `webui-protocol`, `webui-ffi`, and `webui-node`.

1. **No `format!()` in writer output.** Use sequential `writer.write()` calls. `format!` allocates a temporary `String` every invocation.
2. **No `.to_string()` on `Cow`.** Write `Cow<str>` directly to avoid defeating zero-copy.
3. **No `collect::<Vec<_>>()` on splits.** Iterate `path.split('.')` directly. Collecting allocates a `Vec` for sequential access.
4. **No redundant scans.** Use `first_part.len() == path.len()` instead of `path.contains('.')`.
5. **No `String::from(ch)` in escape loops.** Use `ch.encode_utf8(&mut buf)` with a `[u8; 4]` stack buffer, or batch contiguous safe chars into a single write.
6. **No `format!` in hex parsing.** Use direct arithmetic (`(hi_nibble << 4) | lo_nibble`) instead of `u8::from_str_radix(&format!(...))`.
7. **No per-request template re-parsing.** Pre-parse at protocol load time and reuse.
8. **No silent `unwrap_or` defaults.** If `binding_stack.pop()` returns `None`, that's a protocol error - propagate it, don't mask it.
9. **Keep cold error/diagnostic construction off the hot path.** Mark error builders `#[cold]`/`#[inline(never)]` so they don't inline into hot functions and perturb their code layout. Adding cold error code with **no** hot-path work has regressed parse benches ~4-5% purely via layout - keep per-element fast-paths inlinable and push the cold fallback out-of-line. See `skills/diagnostics/SKILL.md` §7.

## Rust - memory rules

1. **No cloning large state trees.** Use `evaluate_with_resolver` with a closure. Cloning duplicates the entire JSON tree in memory.
2. **No cloning HashMaps for scope.** Save/restore only the overwritten key on loop iteration. A HashMap clone copies every entry.
3. **No cloning `String` values for read-only access.** Use `s.as_str()` for `Value::String` branches; only create owned strings for `Number`/`Bool` via a scratch buffer or `Cow`.
4. **Pre-allocate buffers.** Use `Vec::with_capacity` / `String::with_capacity` when size is known or estimable. For HTML output, 4096 bytes is a reasonable starting point.
5. **Prefer `&str` and slices over owned types.** Pass by reference when the callee only reads. Move clone decisions to the caller.
6. **Use `Cow<'_, str>` when a value is sometimes borrowed, sometimes owned.** Avoids unconditional allocation.
7. **No deep-cloning protocol or state per request.** Use `Arc<T>` with clone-on-write or snapshot swapping.
8. **Cap memory for untrusted inputs.** File reads during discovery must have size limits. A 100MB HTML file should not cause OOM.

## TypeScript - `@microsoft/webui-framework` rules

These apply to `packages/webui-framework` (the client-side Web Component runtime).

### Speed

1. **Single-pass hydration.** The framework walks the DOM once to connect all bindings. No multi-pass scanning.
2. **Path-indexed targeted updates.** When an `@observable` changes, only bindings referencing that property are visited - not the entire template.
3. **DOM cloning over innerHTML.** Use `cloneNode(true)` from cached template fragments. Never use `innerHTML` for component creation.
4. **Direct event listeners.** Attach `@event` bindings to the bound element, never a shared render root. `$wireEvents` runs once per block instance, so delegating stacks one listener per block on the same node and fires all of them per dispatch — O(N) for no reduction in listener count.
5. **Microtask coalescing.** Multiple property changes within the same synchronous block batch into a single DOM update via `queueMicrotask`.
6. **Cursor-based repeat reconciliation.** `<for>` block updates use a diff algorithm that only calls `insertBefore` on nodes that actually moved. Append/prepend/remove are O(1).
7. **No `for..in` on objects.** Use `Object.keys()` with an indexed `for` loop — faster and prototype-safe without needing `Object.hasOwn`. Applies to `setState`, `setInitialState`, and any code iterating user-provided objects.

### Memory

1. **No framework in the GC.** Minimize object allocations during reactive updates. Reuse binding objects, don't recreate them.
2. **Template cache is `WeakMap`-keyed.** Parsed template DOMs are cached per metadata object. When metadata is released (e.g., via `Router.gc()`), the cache entry becomes GC-eligible.
3. **No per-update array allocations.** Avoid `.filter()`, `.map()`, `.slice()` in the update hot path. Use index-based iteration.
4. **Strip SSR markers after hydration.** Comment nodes used as markers are removed from the DOM once wiring is complete - they don't persist as memory overhead.
5. **Scope frames are stack-allocated.** `<for>` loop item variables use a linked-list scope chain, not cloned Maps or Objects.

## TypeScript - `@microsoft/webui-router` rules

These apply to `packages/webui-router` (the client-side SPA router).

### Speed

1. **Server does route matching.** The client does not re-match routes. The server returns the matched `chain` array; the client diffs old vs new and mounts only changed components.
2. **Lazy loading via dynamic import.** Route component JS is fetched only on first navigation to that route.
3. **Chain diffing, not full remount.** Navigating between sibling routes preserves parent components. Only the changed level is remounted.
4. **No `for..in` on objects.** Use `Object.keys()` with an indexed `for` loop. `for..in` walks the prototype chain (slow) and requires an `Object.hasOwn` guard to be safe — `Object.keys` is both faster and prototype-safe in one call:
   ```typescript
   // ✗ Bad: slow, prototype-unsafe without guard
   for (const key in obj) { ... }

   // ✗ Still bad: correct but slower than Object.keys
   for (const key in obj) {
     if (Object.hasOwn(obj, key)) { ... }
   }

   // ✓ Good: fast, prototype-safe, no guard needed
   const keys = Object.keys(obj);
   for (let i = 0; i < keys.length; i++) { ... }
   ```

### Memory

1. **Release unused templates.** `Router.gc()` clears cached component templates for routes the user hasn't visited recently. Active route components are never released.
2. **Inventory bitmask prevents duplicate downloads.** The server tracks which component templates the client already has via a bitmask. Re-navigation never re-sends templates.
3. **Minimal state per navigation.** Route-scoped state means the JSON partial contains only what the active route needs, not the full app state.

## Measuring

### Server benchmarks

```bash
cargo bench -p microsoft-webui --bench contact_book_bench          # full run
cargo bench -p microsoft-webui --bench contact_book_bench -- --test # quick validation
cargo bench -p microsoft-webui --bench server_request_bench         # high-level request helper
cargo xtask bench all                                               # all Rust crates
cargo xtask bench node-addon                                        # Node/V8/N-API boundary
```

Compare **Render/1000 P50** before and after. For Node changes, save with
`--save-baseline before` and compare with `--baseline before`. Verify output
**Bytes** is unchanged (same HTML = correct behavior).

### Client performance

```typescript
window.addEventListener('webui:hydration-complete', () => {
  for (const entry of performance.getEntriesByType('measure')) {
    if (entry.name.startsWith('webui:hydrate:')) {
      console.log(`${entry.name}: ${entry.duration.toFixed(1)}ms`);
    }
  }
});
```

### What to report

When making a performance-related change, report:
- **Before/after benchmark numbers** (P50 latency, throughput)
- **Allocation count delta** if measurable
- **Output size** unchanged (proves correctness)
- **Memory profile** for memory-related changes (heap snapshots, RSS delta)