Files
regex-tools/docs/PERFORMANCE.md

152 lines
9.1 KiB
Markdown

# Performance
Live parsing is debounced by 120 ms and live execution by 220 ms. Live runs use
a 250 ms wall-clock limit; manual runs default to 2 seconds and can be raised to
the configured 10-second maximum.
Tests exercise bounded parsing, execution, zero-length iteration, result
truncation and worker recovery. Browser performance depends on hardware,
browser, engine version, pattern and subject. The UI therefore reports observed
elapsed time and exact runtime identity but makes no universal throughput or
safety claim.
PCRE2 cold worker startup includes loading and instantiating the self-hosted
WebAssembly module; warm requests reuse that instance.
Every request still compiles and frees its pattern so no unbounded compiled-code
cache can accumulate. Native match-step, depth and heap limits complement the
supervisor wall clock, and cancellation discards the complete worker heap.
Python has a materially larger cold start because its worker loads the
self-hosted Pyodide JavaScript/WebAssembly runtime and Python standard library
before verifying CPython and `re`. Java imports and self-tests its bundled
TeaVM ES module. Scala compatibility shares that Java module and does not load
a second runtime.
PHP, legacy Perl, Ruby, Go, Rust, C++ and .NET likewise load only when first
selected. Their cold cost differs substantially: PHP, Perl, Ruby and .NET
instantiate larger language/runtime packs; Go includes its standard
WebAssembly runtime; Rust and C++ are comparatively compact native modules.
The UI reports loading separately rather than charging it to the 250 ms live or
selected manual execution timeout.
Warm requests reuse a verified worker. To prevent profile exploration from
leaving every large runtime resident, the supervisor uses a two-worker
retention target and evicts the least recently used idle generation before it
creates another profile worker. An active request is never evicted, so
concurrent active work can temporarily exceed the target. Killing or evicting
a worker discards its complete runtime, so the next request pays the cold-load
cost and verifies identity again.
Automatic-callout tracing is deliberately excluded from every benchmark. It
compiles an independently instrumented PCRE2 pattern in a separate worker and
can materially change runtime work. The trace path retains at most 50,000
complete 32-byte event records and 10 MiB total serialized event/mark bytes;
the viewer renders at most 2,000 retained events.
The production bundle separates syntax and execution workers. Interactive
syntax/extraction trees and editor decoration sets render at most 2,000 items
each and report both the rendered and actual totals. These are presentation
limits only: normalized syntax, exact ranges and bounded engine results remain
available to the capture table and eligible exports.
Replacement templates have a 64 Ki UTF-16 input limit. Every token below that
limit remains in the syntax model; the editor decorates at most 2,000 tokens,
the token list renders 500 rows, and at most 200 replacement diagnostics
(including an omission summary) cross into the UI. List templates have a 16 Ki
UTF-16 input limit and render 250 token chips.
The per-match replacement view renders at most 200 matches and 2,000
contributions, with a combined 50,000 token-evaluation budget. Original,
replacement and individual contribution text are previewed separately from the
bounded output.
Capture rows are collected under the central 100,000-row limit and rendered in
pages of 200. Individual result-value previews are capped at 64 Ki UTF-16 units
and share an 8 Mi-unit aggregate engine-result budget. The extraction tree
shows 100 UTF-16 units per value and capture-table cells show 512, with explicit
range lengths and separate engine-prefix labels; copy and eligible JSON export
retain the bounded engine value. List export is disabled when a source value is
incomplete, so a bounded preview cannot be mistaken for the full value. List
generation additionally stops at 250,000 template-token evaluations, a 64 Ki
UTF-16 row preview, or an 8 Mi-unit aggregate materialization bound. The UI
shows 500 list rows while an eligible export still contains every generated
row; any incomplete result disables export.
Replacement output has a separate 64 MiB bound. Match/capture-limit
incompleteness and output-byte truncation are tracked separately; either makes
the aggregate replacement result incomplete.
Unit-test cases run through supervisors separate from the interactive workers.
Each case uses its configured timeout, cancellation kills the active worker,
and the suite has a 60-second aggregate wall limit. Suite state is capped at
1,000 cases; add, clone, current-result capture and JSON append-import all
enforce that cap and preflight a 32 MiB aggregate in-memory suite bound. Test
names, patterns, subjects, replacement templates and expected values have
explicit per-field limits; subjects must also fit the execution engine's UTF-8
byte cap. Failure diagnostics preview only bounded portions of expected and
actual values and retain at most 2 KiB UTF-8 per result, keeping 1,000 result
messages below the 2 MiB log budget. A complete replacement output above the
expected-value or aggregate-suite limit is not copied into draft state; a
lightweight `should-match` draft is offered when applicable.
Project import, export and local save have a 32 MiB aggregate UTF-8 JSON limit
in addition to field-specific limits. Standalone test-suite import has the same
32 MiB pre-parse file/document limit. Export and save count the indented JSON
representation against a byte budget before materializing or writing it.
IndexedDB schema version 2 indexes `updatedAt`; latest-project loading opens one
descending cursor and validates only that record instead of calling `getAll()`.
Corpus runs accept at most 256 documents, 16 MiB per document and 256 MiB of
aggregate UTF-8 input. Jobs run sequentially so only one engine request is
active at a time. A batch retains at most 100,000 matches and 64 MiB of applied
output, and stops after five minutes of aggregate wall time. Each document also
uses the selected manual timeout. Progress is reported after every document;
cancellation kills the current worker and labels all remaining documents.
Result tables contain summaries, not match/capture objects. ZIP export is
available only for an all-exact apply batch and is created asynchronously.
Independent-line mode preflights at most 100,000 logical lines and processes
each as an isolated engine subject while preserving line separators for exact
apply output. Per document, corpus extraction retains at most 32 group summaries
(including the full match), three 160-UTF-16-unit samples per group and 100
unique diagnostics. Output previews show 2,000 units. Content-free summary
exports are capped at 4 MiB; the applied archive is capped at 68 MiB in addition
to the 64 MiB uncompressed-output budget.
ECMAScript analysis reports cold compilation separately from warm native
execution. The default benchmark uses three warm-up and 15 measured samples;
user caps are 100 warm-ups and 1,000 samples. It reports median and
nearest-rank p95 values plus separate timing and outcome/growth plots with the
exact runtime identity. Each sample defaults to a 2-second killable-worker
limit (10-second cap), and the aggregate analysis wall time is 30 seconds.
Growth probes retain at most 24 steps, generate at most 1 MiB by default under
the central 16 MiB subject cap, and reject more than 10,000,000 repetitions
before string allocation. Replacement timing is skipped when match collection
is truncated or the estimated output exceeds 8 Mi UTF-16 units. Results are
observations for the chosen runtime and generated inputs, never a general
complexity proof. See [`ANALYSIS.md`](ANALYSIS.md).
Generated-case synthesis defaults to 48 retained cases from at most 192
candidates, 16 seeded-random variants, 64 KiB per candidate, 1 MiB aggregate
candidate bytes and 15 seconds of aggregate wall time. Every candidate then
uses a separate selected-engine request with a 500 ms default timeout.
Generation preflights UTF-8 size and repetition bounds, caps normalized-AST
traversal at 20,000 nodes / 512 levels and retains only bounded diagnostics.
Hard caps are documented in [`GENERATED_CASES.md`](GENERATED_CASES.md).
Subject minimization accepts at most 1 MiB UTF-8, performs at most 2,000 real
engine candidate evaluations and stops after at most 60 seconds including
syntax setup. One fixed worker timeout of at most 10 seconds applies to every
candidate. A candidate starts only when that complete deadline fits in the
remaining aggregate budget. Accepted-transform history retains 200 entries;
evaluation and accepted totals remain exact. See
[`MINIMIZATION.md`](MINIMIZATION.md).
Formatter validation uses two syntax supervisors and two actual-engine
supervisors so source/candidate outcomes cannot share compiled state. The
current subject/replacement snapshot always runs; at most 1,000 enabled tests
with the exact active identity are replayed. Test validation has a 60-second
aggregate wall budget, and a test starts only when its complete configured
worker timeout still fits. Preview details retain at most 10,000 transforms and
the UI renders the first 200. See [`FORMATTING.md`](FORMATTING.md).