Warm up WASM scopes before running MozJS queries

XMLWordPrintableJSON

    • Type: Task
    • Resolution: Unresolved
    • Priority: Major - P3
    • None
    • Affects Version/s: None
    • Component/s: None
    • Query Integration
    • None
    • None
    • None
    • None
    • None
    • None
    • None

      Telemetry insights → tuning & future optimizations

      From the 11600001 (per-invoke) and 11600002 (scope-create) data:

      1. Per-invoke $where is cheap and stable (~20–225µs, flat linearMemoryBytes/gcHeapBytes). So the compiled-function cache and steady-state execution are fine — no tuning needed on the invocation path, and the "memory growth" hypothesis is disproven. Good to know we don't need to touch GC/reuse-count limits for correctness.
      1. idleBridgeAgeMs: -1 on ~every cold rebuild — the biggest insight. The thread-local IdleBridge cache almost never hits because queries don't land on a thread that holds a parked bridge (connection-thread churn). So kMaxBridgeIdleTime (1s) is mostly irrelevant — bumping it wouldn't help this workload, because the bridge is usually absent, not expired. Tuning kMaxBridgeIdleTime is a red herring for spaced/multi-connection workloads.
      1. Post-fix, the residual per-query cost is "B" (~68ms Linux) = Store instantiate + SpiderMonkey init, since engineCtx is now shared (~4µs). That's the next optimization target if we want steady-state $where faster.

      Future optimizations (ranked by leverage, to track)

      1. Pre-warm the shared context at startup (already ticketed) — hides the one-time deserialize; also lets the test drop its warm-up line.
      2. Cross-thread bridge/Store pool — the real fix for the idleBridgeAgeMs:-1 miss. A shared pool of instantiated bridges (vs the current thread-local one) would let queries skip the ~68ms B, not just the deserialize. This is the natural evolution of your "pre-warmed bridge instances" idea, and the telemetry shows it's where the remaining win is.
      3. Reduce SpiderMonkey per-Store init cost (B) — engine-level; only worth it if (2) proves insufficient.
      4. kMaxBridgeReuseCount (100) / kMaxBridgeIdleTime (1s) tuning — low priority; telemetry shows reuse rarely triggers at all, so these knobs don't move the needle until (2) makes reuse effective.

      Query overhead breakdown, post-fix

      Using the fix-run numbers (11600002 + 11600001, Linux):

      Component Cost Share of a cold-scope $where (100 docs)
      Engine-context lookup (what we fixed) ~4µs ~0.006% (negligible)
      Cold Store startup (Store instantiate + SpiderMonkey init, "B") ~68ms ~85–90%
      Per-document execution (100 × ~60–170µs) ~6–17ms ~10–15%

      So with the shared context handled, cold Store startup is now ~85–90% of a cold-scope query's overhead (~68ms of ~78ms), the engine-context term we eliminated is effectively zero, and per-invoke execution is the small remainder. (macOS absolute values are larger, but the proportion holds — B dominates.)

      Two important caveats:

      • This is the cold path (bridge reuse missed). When the thread-local bridge is reused, Store startup is skipped (resetRealm, ~fast) and the query is dominated by per-invoke instead. But reuse rarely hits here (idleBridgeAgeMs:-1), so most queries are cold → Store startup dominates.
      • That's precisely why the ranked next optimization is the cross-thread bridge/Store pool: it targets that ~85–90% by letting queries reuse an already-instantiated Store across threads, not just the shared engine.

            Assignee:
            Calvin Nguyen
            Reporter:
            Calvin Nguyen
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: