From 325c75b58c05e9ef8fed51c05609dfc2bfed112d Mon Sep 17 00:00:00 2001 From: nsfisis Date: Wed, 19 Aug 2026 23:45:38 +0900 Subject: perf(semver): memoize MultiConstraint's string form composer/semver memoizes MultiConstraint::__toString() into $this->string, but the port recomputed it on every call. On `require laravel/laravel` that meant 837k stringifications visiting 1.7M child constraints, all of them feeding the CompilingMatcher and Intervals cache keys. Co-Authored-By: Claude Opus 5 (1M context) --- crates/shirabe-semver/src/constraint/multi_constraint.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/shirabe-semver/src/constraint/multi_constraint.rs b/crates/shirabe-semver/src/constraint/multi_constraint.rs index 4e9b8cf5..30c2edd6 100644 --- a/crates/shirabe-semver/src/constraint/multi_constraint.rs +++ b/crates/shirabe-semver/src/constraint/multi_constraint.rs @@ -4,11 +4,13 @@ use crate::constraint::AnyConstraint; use crate::constraint::Bound; use crate::constraint::MatchAllConstraint; use shirabe_php_shim::CmpOp; +use std::sync::OnceLock; #[derive(Debug, Clone)] pub struct MultiConstraint { pub(crate) constraints: Vec, pub(crate) pretty_string: Option, + string: OnceLock, pub(crate) conjunctive: bool, } @@ -28,6 +30,7 @@ impl MultiConstraint { Self { constraints, pretty_string, + string: OnceLock::new(), conjunctive, } } @@ -251,8 +254,11 @@ impl MultiConstraint { impl std::fmt::Display for MultiConstraint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let parts: Vec = self.constraints.iter().map(|c| c.to_string()).collect(); - let sep = if self.conjunctive { " " } else { " || " }; - write!(f, "[{}]", parts.join(sep)) + let string = self.string.get_or_init(|| { + let parts: Vec = self.constraints.iter().map(|c| c.to_string()).collect(); + let sep = if self.conjunctive { " " } else { " || " }; + format!("[{}]", parts.join(sep)) + }); + f.write_str(string) } } -- cgit v1.3.1-4-g156e