diff --git a/Cargo.lock b/Cargo.lock index a748b33e8a..ab30bfef1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4776,9 +4776,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perex" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1473d470930ed48574515a25df34900f3af89c6fa422d903e019121312a9f13e" +checksum = "4cacad0d8fd77338345abb71e4f8372ce93bf0e9205d845940f8e789b3fc98a8" [[package]] name = "perry" diff --git a/Cargo.toml b/Cargo.toml index e0e0fafe70..c2feb89f08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -381,7 +381,20 @@ regex = "1.12" aho-corasick = "1.1" memchr = "2" # The single regular-expression engine, through crates/perry-perex. -perex = "0.1.9" +# 0.1.11 carries PerryTS/perex#3 (package-shaped repeats and classes leave the +# per-character phase loop; release commit 80845f5310, tag v0.1.11). It is +# younger than the 7-day `global-min-publish-age` soak (.cargo/config.toml), +# so Cargo.lock was resolved once with CARGO_RESOLVER_INCOMPATIBLE_PUBLISH_AGE=allow +# under an owner-approved one-time publish-age override (2026-09-28), for this +# version only; the window itself is unchanged. No public API change from +# 0.1.10. Published 2026-09-27T21:13:35Z, sha256 +# 4cacad0d8fd77338345abb71e4f8372ce93bf0e9205d845940f8e789b3fc98a8 — the +# checksum Cargo.lock records, verified against +# https://crates.io/api/v1/crates/perex/0.1.11. As with turnloop above, the +# opt-out is a committed, reviewable change, never a silent one, and the lock +# pins the exact artifact. The override lapses on its own on 2026-10-04, when +# 0.1.11 clears the window. +perex = "0.1.11" # turnloop P0/P1: the per-agent event-loop wait driver and, since P1, Perry's # stream sockets (docs/turnloop/p0-report.md, crates/perry-runtime/src/turnloop_net). # 0.1.0-alpha.3 switched turnloop's own dependency requirements from exact pins diff --git a/changelog.d/11548-perex-0111-package-regex-shapes.md b/changelog.d/11548-perex-0111-package-regex-shapes.md new file mode 100644 index 0000000000..9318e4fedd --- /dev/null +++ b/changelog.d/11548-perex-0111-package-regex-shapes.md @@ -0,0 +1,5 @@ +### Performance + +- The regex engine moves from perex 0.1.9 to 0.1.11. 0.1.11 carries PerryTS/perex#3, which takes package-shaped repeats and character classes out of the matcher's per-character loop. Measured with `perf stat -e instructions:u` (two-N differential) on perrymaster against `main`: uuid's `validate` regex −65.2% per call, jws's `JWS_REGEX.test` −84.3%, `/^\d+$/.test` −5.4%, and on the package workloads jsonwebtoken/decode −61.1%, uuid/v4 −47.9%, uuid/v7 −34.4%, nanoid/generate −30.4% and uuid/v5_parse −28.4%. Every output is byte-identical to Node 26.5.1, and peak RSS does not move beyond run-to-run noise (#10166). +- perex 0.1.10's `Search::run` fails with `RunError { error, remaining_work, buffers }`. Perry's two search sites now record the work a failed run left instead of keeping the budget they started with. Nothing observes this today, because the budget is `usize::MAX` on these paths. +- perex 0.1.11 is younger than the 7-day cargo `min-publish-age` soak. The owner approved a one-time override for this version on 2026-09-28. `Cargo.lock` was resolved once with `CARGO_RESOLVER_INCOMPATIBLE_PUBLISH_AGE=allow`, and the `Cargo.toml` comment records the approval, the publish time and the sha256 the lock pins, checked against the crates.io API. The soak window is unchanged. diff --git a/crates/perry-runtime/src/regex/perex_runtime.rs b/crates/perry-runtime/src/regex/perex_runtime.rs index 9cafb56082..f2b8f9a6cf 100644 --- a/crates/perry-runtime/src/regex/perex_runtime.rs +++ b/crates/perry-runtime/src/regex/perex_runtime.rs @@ -422,18 +422,14 @@ fn find_near_lent<'mem, S: ImmutableSubject>( // Both views are acquired once for the quantum that decides nearly // every per-call search; a `Search` is built and moved only if this // one pauses or asks for more scratch. - // On an error that is not a capacity request -- WorkLimit, - // InvalidProgram, ChangedResources, Cancelled -- `run` returns Err and - // drops the scratch and the remaining budget, where `new` + `advance` - // left a Search to read `remaining_work()` from. So `*budget` keeps its - // entry value and under-counts what the failed call spent. WORK is - // usize::MAX on every path here, so nothing observes it today; if a - // finite execution budget is ever reintroduced (see #10164/#10165), - // this stops being free and wants the engine's failure arm to report - // remaining work. - let mut search = match Search::run(resources, start, near, scratch, *budget, quantum) - .map_err(search_error)? - { + // A failed run reports the work it left (perex 0.1.10), so the budget + // records what the failed call spent, as a failed search did. + let run = + Search::run(resources, start, near, scratch, *budget, quantum).map_err(|failed| { + *budget = Budget::new(failed.remaining_work); + search_error(failed.error) + })?; + let mut search = match run { Run::Finished(mut finished) => { *budget = Budget::new(finished.remaining_work()); let position = finished.position(); @@ -554,18 +550,14 @@ pub(crate) fn find_near<'mem, S: ImmutableSubject>( poll()?; let buffers = MatchBuffers::new(memory, size)?; - // On an error that is not a capacity request -- WorkLimit, - // InvalidProgram, ChangedResources, Cancelled -- `run` returns Err and - // drops the scratch and the remaining budget, where `new` + `advance` - // left a Search to read `remaining_work()` from. So `*budget` keeps its - // entry value and under-counts what the failed call spent. WORK is - // usize::MAX on every path here, so nothing observes it today; if a - // finite execution budget is ever reintroduced (see #10164/#10165), - // this stops being free and wants the engine's failure arm to report - // remaining work. - let mut search = match Search::run(&resources, start, near, buffers, *budget, quantum) - .map_err(search_error)? - { + // A failed run reports the work it left (perex 0.1.10), so the budget + // records what the failed call spent, as a failed search did. + let run = + Search::run(&resources, start, near, buffers, *budget, quantum).map_err(|failed| { + *budget = Budget::new(failed.remaining_work); + search_error(failed.error) + })?; + let mut search = match run { Run::Finished(mut finished) => { *budget = Budget::new(finished.remaining_work()); let position = finished.position(); diff --git a/tests/release/packages/next-app-route/provider/Cargo.lock b/tests/release/packages/next-app-route/provider/Cargo.lock index d07f754d08..018b77bbd6 100644 --- a/tests/release/packages/next-app-route/provider/Cargo.lock +++ b/tests/release/packages/next-app-route/provider/Cargo.lock @@ -2960,9 +2960,13 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perex" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1473d470930ed48574515a25df34900f3af89c6fa422d903e019121312a9f13e" +checksum = "4cacad0d8fd77338345abb71e4f8372ce93bf0e9205d845940f8e789b3fc98a8" + +[[package]] +name = "perry-abi" +version = "0.5.1654" [[package]] name = "perry-base64" @@ -3132,6 +3136,7 @@ dependencies = [ "node-semver", "notify", "perex", + "perry-abi", "perry-base64", "perry-diagnostics", "perry-dispatch",