Files
regex-tools/docs/PERFORMANCE.md

9.1 KiB

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.

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.

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.

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.