Performance

Designed to stay off the critical path.

0ms

INP Impact

Across 12 stress configs (3 assertion counts × 2 churn rates × 2 CPU profiles)

1.7KB

Idle Heap

Observer, listeners, sweep timer

2.2ms

Worst P99

4% of the 50ms long-task threshold

0

Long Tasks

Never creates one, any scenario

MutationObserver Callback Scaling

The agent's hot path — stress benchmark measures callback duration directly. Most production deployments run 10–50 assertions per page; the tables below stress-test up to 1,000.

Unthrottled (Apple M4 Pro)

AssertionsChurnP50P95P99
500/s0ms0.3ms0.5ms
50100/s0ms0.1ms0.3ms
2000/s0ms0.3ms0.5ms
200100/s0ms0.1ms0.3ms
10000/s0.1ms0.6ms0.8ms
1000100/s0.1ms0.2ms0.6ms

CPU 4x Throttle (simulating mid-tier mobile)

AssertionsChurnP50P95P99
500/s0ms0.7ms1.1ms
50100/s0ms0.7ms1.0ms
2000/s0ms0.7ms1.1ms
200100/s0ms0.7ms0.9ms
10000/s0.3ms1.1ms2.2ms
1000100/s0.4ms1.0ms1.4ms

In most configurations, higher churn rates produce lower P95 values — the MutationObserver batches mutations, and more frequent small batches are cheaper to process than fewer large ones.

Heap Overhead

Sub-linear scaling. 20x more assertions costs 1.8x more memory.

AssertionsChurnHeap (unthrottled)Heap (CPU 4x)
Idle—+1.7KB—
500/s+76KB+69KB
50100/s+92KB+82KB
2000/s+115KB+120KB
200100/s+150KB+152KB
10000/s+116KB+117KB
1000100/s+140KB+102KB

The agent's internal GC sweep (every 5 seconds) cleans up resolved assertions, keeping the working set bounded.

First Paint Impact

LCP impact is not statistically significant below 200 assertions. At 1,000 assertions — a deliberately extreme stress case — the initial attribute scan adds 6–12ms (one frame at 60fps).

For context: a typical instrumented page has 10–50 assertions. The 1,000-assertion test exercises the agent at 20–100x normal density to establish a scaling ceiling.

Caveats

What the benchmarks don't cover.

Methodology

How we measure.

Each benchmark run launches a fresh Chromium instance per pair of measurements. Within each pair, condition A (page without Faultsense) and condition B (page with Faultsense) share the same browser process so V8 isolate noise cancels out in the differential.

Runs are strict A-B-A-B interleaved. The first pair is discarded as JIT warmup, leaving 29 usable pairs (demo) or 14 usable pairs per configuration (stress). Each measurement navigates to the target URL, waits for load, then soaks for 60 seconds.

Heap measurements use Chrome DevTools Protocol — not performance.memory which would pollute the target isolate. Two forced GCs run before each heap read. CPU profiler sampling interval is 100µs.

Core Web Vitals are captured via the web-vitals v4 library with explicit finalization via a synthetic visibilitychange event.

Statistical significance uses the Wilcoxon signed-rank test (two-tailed, non-parametric) on paired A-B differences. 95% confidence intervals use the Hodges-Lehmann estimator over Walsh averages.

An A-vs-A baseline (both conditions without the agent) validates measurement stability: 0ms LCP delta (p=0.351), +16 bytes heap (p=0.875). The apparatus does not create false positives.

Observer Integrity

The agent does not modify DOM nodes outside its own script element — it reads mutation records and attribute values, never writes to them. Events are dispatched via sendBeacon to reliably deliver without blocking navigation or user interactions.

The overhead numbers above confirm the observer does not perturb the signals it collects.

Run it yourself.

Clone the repo, run the benchmarks, get the same numbers.

# Demo benchmark (30 pairs, ~2 hours)
npm run benchmark:demo

# Stress benchmark (15 pairs, ~4 hours)
npm run benchmark:stress

# A-vs-A measurement validation
npm run benchmark:ava

Benchmark tool, stress harness, and raw data: github.com/Faultsense/faultsense-agent

Full statistical reports with p-values and confidence intervals: faultsense.org/docs

Test Environment

Agent v0.4.0 · Apple M4 Pro (14 cores) · 48GB RAM · Chromium 147.0.7727.15 · Node v22.21.0 · Playwright 1.59.1