Skip to content

perf: OpenCode native binary startup must beat the bun binary — attribute and remove the module-init cost (measured 13× bun CPU on the Aug 21 build) #10106

Description

@proggeramlug

Parent: #10107. Related: PERRY_STARTUP_PLAN.md (PR #10066 tiny-path work), #6532/#6533/#6534 (cc --version 227 ms), #9441 (idle tail), scavenge/tenuring notes.

Measured 2026-09-12 (dev Mac, load avg ~100 — wall inflated; CPU is the fair number)

binary --version wall user CPU max RSS
official bun-compiled opencode 1.17.7 1.0–1.5 s 0.54 s 185 MB
bun run src/index.ts --version (transpiles on the fly) 2.8 s 0.85 s
perry Aug-21 binary (minimal profile, 2,454 modules, 213 MB, perry 0.5.1512) 15–29 s 6.8–7.0 s 563 MB
same, --help 14.8 s 7.0 s
A sample of the stripped binary shows the main thread 100 % in generated code (no wait states), i.e. eager module initialization of the graph (Effect's 225 modules build Schema/Layer/Context objects at import; drizzle tables; yargs command tree). No startup number exists for the full graph (4,063 modules on v1.18.30; 7,136 on the Windows full build #9133), so expect worse before optimization.

Target

On the quiet Mac mini (perry@perry-macos.local), same host, five warm runs each, paired A/B per PERRY_STARTUP_PLAN S1 discipline:

  • opencode --version: user CPU ≤ 0.5 s, wall ≤ 1.0 s, RSS ≤ 185 MB (beat the official v1.18.30 binary from gh release download v1.18.30 -R anomalyco/opencode -p 'opencode-darwin-arm64.zip').
  • opencode --help and TUI time-to-first-frame: below bun.

Scope

  1. Attribute: symbols build (strip=none, PERRY_MAIN_STACK_MB if needed), sample/Instruments over --version, plus PERRY_DEBUG_INIT module-init trace; produce a per-module init-cost table (top 50) and a per-runtime-helper table (class registration, closure allocation, object-literal construction, string interning, regex compile — regex construction is eager and SipHash-keyed per pattern in perry, see the regex analysis note in secret-tests memory, GC minors during init).
  2. Lazy work: anything perry does per module that bun does not (e.g. eager class/shape registration, module-global slot promotion, side-table setup) becomes lazy or batched; check the young-gen / tenuring configuration during init (563 MB RSS vs 185 MB suggests allocation volume, not just code size).
  3. Graph-level: confirm the compiled graph does not initialize modules bun would never evaluate (dynamic-import-only subgraphs such as Code Mode/typescript, prettier, @babel/* via the runtime Solid plugin — must not be in the eager init order; fix(compile): support full OpenCode source builds #9133 kept dynamic imports out of eager init, verify on this graph).
  4. Binary size / page-in: 213 MB → the first-run variance (16→29 s) hints at page-in; measure cold vs warm and consider PERRY_LL_SIZE_OPT=1 + optnone threshold (perf(compile): reduce generated bundle bloat #8418) trade-offs.
  5. Re-measure after each lever with the A/B script; keep raw samples in secret-tests/opencode-1.18.30-inventory/perf/.

Acceptance

  • Attribution report with the top-50 module-init costs and the runtime-helper breakdown, on the full v1.18.30 graph.
  • --version user CPU and RSS below the official bun binary on the Mac mini, two independent batches.
  • TUI first frame not slower than bun.
  • No correctness regressions: the 22 node startup oracles (PERRY_STARTUP_PLAN) and the cc --help parity gate stay green.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew capability or improvementperformanceRuntime, compile-time, build-size, or memory performance

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions