本文へ移動
cccskills
無料GitHub で公開

performance-audit

Audit or defend Octane performance. Use when a change can affect per-render, per-node, scheduling, compiler-output, SSR, hydration, or bundle cost, or when asked whether something is fast enough. Holds the V8-shape, DOM, and scheduling rules hot-path code must follow.

インストール方法を見る

含まれるファイル(4)

  • SKILL.md10.8 KB
  • references/dom-work.md5.0 KB
  • references/scheduling.md9.4 KB
  • references/v8-shapes.md9.6 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Skill: Octane performance audit

Use this to investigate performance regressions, benchmark results, scheduler/reconciler overhead, compiler output quality, or ecosystem binding perf.

Read first

  • Benchmark README in the affected benchmarks/* directory
  • packages/octane/src/runtime.ts comments for runtime-level changes
  • Existing benchmark scripts in benchmarks/*/package.json and run.mjs
  • The discipline reference for each dimension the change touches: V8 shapes and allocation, DOM work, and scheduling

Hot-path discipline

These rules apply to code that runs per render, node, item, event, signal notification, or server request. Each reference cites the runtime code that already follows the rule.

  • Shapes: allocate hot records from one constructor or one literal site with every field present, in a fixed order, as BlockImpl and the bagN factories do. No delete, conditional keys, runtime class fields on hot classes, or per-instance freezing. Keep call sites and return shapes monomorphic, numeric fields integral, and arrays packed. Do not allocate closures, literals, rest arrays, or iterators per item.
  • Reachability: never name a heavy function from a hot compiled path. Put feature-only code behind the capability or driver that owns it.
  • Member reads: look for repeated property reads and member-chain prefixes in hot code and emitted JS. Prefer a local const for a stable value used more than once; follow Reuse stable member reads.
  • DOM: read geometry before writing, never in the render walk, and never interleaved with writes in a loop. Insert built subtrees once. Keep events native and delegated. Write from resize callbacks only through createResizeObserver.
  • Scheduling: a microtask, await of a settled value, or requestAnimationFrame is not a yield. Do not add a render or commit per microtask hop. Coalesce first, then yield by posting a task through an existing poster. Leave the documented scheduler contract to issue #1864.

Run the perf-review skill on the diff before handoff. It applies these rules to the change and lists the evidence each finding needs.

Workflow

  1. Define target

    • Scenario: mount, update, keyed reorder, context, effects, Suspense, hydration, SSR, binding package.
    • Metric: runtime duration, allocations, DOM operations, bundle size, compiler output size, benchmark score.
    • Baseline: current main, previous commit, React, Solid/Ripple comparison, or documented expectation.
    • Semantic control: the output, identity, ordering, or lifecycle result that proves both candidates perform the same work.
  2. Choose harness

    • Existing benchmarks: node benchmarks/bench.mjs --list names every suite. Common ones are js-framework, dbmon, news, recursive-context, signal-favoring, and todomvc.
    • Object shapes: benchmarks/runtime-object-shapes gates one map per record family with %HaveSameMap. Tier and deopt traces: benchmarks/client-hot-paths/functions.mjs.
    • Scheduling: scheduler-responsiveness, passive-scheduling, effect-scheduling, and the marker-task commit count in scheduling.
    • Micro regression: focused Vitest with counters/logging.
    • Compiler output: inspect emitted JS from compile.js/Vite transform.
    • Browser-only perf: use Playwright or benchmark harness if available.
  3. Run baseline and candidate

    • Warm up.
    • Run multiple iterations.
    • Record environment and command.
    • Avoid mixing dependency install/build changes with code changes.
    • Use the same commit inputs, runner options, and machine state. Do not compare a quick smoke result with a full result.
    • Treat a delta inside observed variance as inconclusive. Prefer ratio guards and deterministic counters when wall-clock noise is larger than the claim.
    • The pull request benchmark gates js-framework production calls and DOM mutations per operation against the merge commit's first parent: any increase fails it. Wall time there is a paired report, called slower or faster only when its 95% interval lies beyond ±3%.
  4. Diagnose

    • Runtime hot paths: scheduler queues, effect flushing, keyed reconciliation, event delegation, context propagation, refs.
    • Compiler hot paths: unnecessary deopts, over-broad dynamic regions, missed folding, slot churn, repeated closures.
    • Binding hot paths: excessive subscriptions, selector equality failures, layout-effect loops.
  5. Patch or report

    • Prefer measurable changes with a regression test/benchmark note.
    • Preserve correctness over micro-optimizations.
    • Document tradeoffs and residual risk.
  6. Challenge the conclusion

    • Inspect whether work was shifted to startup, compilation, hydration, garbage collection, or a less visible branch rather than removed.
    • Check allocation lifetime and invalidation for new caches or memoization.
    • Attempt a workload that should make the proposed improvement disappear; if it does not, look for a harness or measurement error.
    • Re-run the final candidate after self-review changes. Never report a stale intermediate measurement as the final result.

Reuse stable member reads

Repeated node.firstChild, node.nextSibling, record.field, or object[key] reads can repeat accessor work and duplicate property names in emitted code. Cache a reused value in a local declaration at its first needed read, in the smallest scope covering its uses. Prefer this simple reuse over a persistent cache or a new helper abstraction; do not alias every one-off property read.

For a native DOM node with no intervening tree mutation:

// Before: read the same first child up to three times.
if (node.firstChild !== null && node.firstChild.nodeType === 3) {
  return node.firstChild;
}

// After: read once and reuse the result.
const firstChild = node.firstChild;
if (firstChild !== null && firstChild.nodeType === 3) {
  return firstChild;
}
  • Prove the receiver, computed key, and value stay stable across all uses. A getter or proxy may have observable effects or return a different value on each read; evaluating the receiver or coercing the key can also have effects. Reducing these evaluations is not automatically equivalent.
  • Preserve evaluation order, null guards, and short-circuit behavior. Do not hoist a read onto a path that previously skipped it or before its guard.
  • Re-read after DOM or state mutation, callbacks or reentrant calls, and await/yield boundaries that can invalidate the value. In loops, cache per iteration unless stability across iterations is established; a removal loop must observe the new firstChild after each removal.
  • Keep Octane's existing access semantics: where code uses getFirstChild, getNextSibling, or staged DOM views, reuse that result instead of switching to a raw native read. Compiled template walks can reuse stable chain prefixes without routing every access through a shared helper.
  • Check the emitted and minified JS, including raw and compressed size, before claiming a size win. A local declaration can cost more than it saves, and a JIT or minifier may already eliminate some repeated reads. Use the owning benchmark for runtime claims and relevant correctness checks when changing code; fewer source-level reads alone do not establish a speedup.

Bundle bytes

  • There are no committed byte budgets, and no check fails on bytes. The pull request benchmark report lists every byte change; read the rows your change moved and justify any growth in the pull request description.
  • Keep growth small anyway: move hydration-only or feature-only code behind the capability that owns it. Judge growth by raw and gzip; brotli can grow when code is removed.
  • Measure while iterating with node benchmarks/bundle-size/run-minimal.mjs [scenario...] and node benchmarks/bundle-size/run.mjs octane-tsrx octane-jsx.

Evidence required for hot-path changes

ChangeEvidence
Any runtime, compiler-output, or binding hot pathThe pull request benchmark report (.github/workflows/pr-bench.yml): byte changes, reported only, and js-framework production calls and DOM mutations per operation, where any increase fails.
Bundle bytesnode benchmarks/bundle-size/run-minimal.mjs <scenario> and run.mjs octane-tsrx octane-jsx while iterating. CI's report rows are authoritative: brotli, and occasionally gzip or raw for path-dependent scenarios, can differ locally.
A hot record's shape%HaveSameMap across every construction mode, as benchmarks/runtime-object-shapes does, and perf-review-scan clean.
Allocation or tieringA scratch harness on the production bundle: pinned semi-space for bytes per call, %GetOptimizationStatus and --trace-deopt for tiers.
Scheduling, commits, or effect timingThe marker-task commit count from scheduling, plus the relevant scheduling suite.
User-visible latency claimsEvent Timing in Chromium, maximum duration per interactionId, against React on the same app. Long-task entries are not evidence.
Optimization claims in generalnode benchmarks/bench.mjs <suite> --ratios for the suite that owns the scenario.

Run locally only the suite or scratch probe that owns the scenario, one suite at a time (--quick while iterating). Leave wide runs to CI: the full pnpm test, the full benchmark sweep, and end-to-end or browser suites. Parallel agent sessions share one machine, and a wide local run makes every timing on it noise.

Report template

## Performance audit
- Target: ...
- Baseline command/result: ...
- Candidate command/result: ...
- Delta: ...

## Findings
- ...
- `perf-review` result: ...

## Recommendation
- ...

## Validation
- ...

## Confidence and residual risk
- Noise/variance: ...
- Modes not measured: ...
- Alternative explanation considered: ...

Common pitfalls

  • jsdom is poor for layout/paint measurements.
  • A microtask-level change can look free in a benchmark that awaits each operation, and still add a commit per hop under a burst. Count commits before a marker task.
  • V8 trace flags piped to a busy parent lose records. Write traces to a file.
  • Differential innerHTML tests prove correctness, not performance.
  • React and Octane may perform different physical DOM move sets while producing identical final DOM.
  • Compiler output changes can shift runtime cost; inspect both layers.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Write a new Octane component in .tsrx, convert JSX to .tsrx, or add scoped <style> blocks and themes. Use when creating a .tsrx file rather than editing one, because the path-scoped tsrx-authoring rule only fires once such a file is open.

日本語の概要は準備中です。原文の説明を表示しています。

octanejs/octane1,4572026年10月11日 更新

Find and fix a bug in the octane monorepo. Use when given a failing test, a regression, or a report that behavior differs from expectation, including deciding whether a React mismatch is an intentional divergence.

日本語の概要は準備中です。原文の説明を表示しています。

octanejs/octane1,4572026年10月11日 更新

Keep a change small and in the existing idiom - size the plan and weigh smaller alternatives before writing it, reuse the mechanism that already owns the behavior instead of adding a parallel one, and simplify the diff before handoff. Use when planning any change beyond a few lines, before readying a PR, or when reviewing agent-written code for bloat.

日本語の概要は準備中です。原文の説明を表示しています。

octanejs/octane1,4572026年10月11日 更新

Open a pull request for octane work. Load before creating the branch, commit, changeset, or PR, including when opening a PR is the tail step of a task that was about something else.

日本語の概要は準備中です。原文の説明を表示しています。

octanejs/octane1,4572026年10月11日 更新

Work a GitHub issue in the octane repo. Use when given an issue number or link. Fetches the issue, classifies the owning area, and plans the fix.

日本語の概要は準備中です。原文の説明を表示しています。

octanejs/octane1,4572026年10月11日 更新

Change Octane's runtime, compiler, scheduler, reconciler, SSR, or hydration engine. Use before editing packages/octane/src. Covers the observable contract, hot-path analysis, and the performance evidence required before handoff.

日本語の概要は準備中です。原文の説明を表示しています。

octanejs/octane1,4572026年10月11日 更新

octanejs のスキルをすべて見る

このスキルの問題を報告する