Performance ยท profile first, keep same-run controls
Performance is a release gate now, not an after-the-fact chart.
Minify++ has three JavaScript cost profiles: a latency-sensitive conservative default, a structured scope-aware mode, and an aggressive bounded optimizer. Recent development made performance regressions an explicit rejection condition and added reproducible stage profiling plus same-run competitor controls.
The current rule
Any real-bundle post-validation failure rejects a checkpoint. Aggregate throughput regressions above 2% are inspected, above 5% are normally rejected, and a regression above 10% on a large representative fixture is rejected unless a measured trade-off is deliberately accepted.
Historical GitHub milliseconds are not treated as a universal baseline. The exact same Minify++ revision measured materially differently across runner epochs, so current JavaScript comparisons refresh OXC, SWC, esbuild and Terser in the same benchmark job.
What the latest performance campaign changed
- Reserved predictable syntax-analysis storage to avoid repeated container growth.
- Indexed
var/function redeclaration lookup inside scopes. - Propagated function ownership once instead of repeatedly walking scope ancestry.
- Skipped semantic-fact construction in structured mode when that data is not consumed.
- Avoided rebuilding an already-successful binding-renaming plan during later aggressive rounds.
- Rejected a broad reference cache and a class-ancestry shortcut when they failed to produce safe representative wins.
The important result is the development process as much as the code: plausible optimizations that did not survive semantic or representative performance gates were removed rather than accumulated.
Real-bundle result
The external 12-artifact JavaScript benchmark validates conservative, structured and aggressive output independently. The current performance-certified checkpoint passed all 36 / 36 mode/artifact validations.
Against the preceding same-run-controlled baseline, environment-normalized median throughput improved by approximately 11.6% in default mode, 12.0% in structured mode and 20.1% in aggressive mode. These are comparative checkpoint figures, not portable speed promises.
Profile the real stages
make profile-js FILES="bundle.js" RUNS=7
MINIFY_PROFILE_STAGES=1 \
.build/js-perf-profile --runs 3 --mode aggressive bundle.js Stage attribution covers scanner/emission, concrete syntax construction, scope graph construction, reference resolution and semantic-fact construction. Profiling is opt-in and does not alter normal API output.
Internal per-format regression gate
make benchmark remains useful for format-level throughput, output size and RSS regression tracking. Those generated fixtures are direction-finding probes; the external 12-bundle JavaScript benchmark is the stronger acceptance oracle for structured/aggressive work.
Performance philosophy
- Never trade semantic preservation for a favorable timing.
- Compare old/new Minify++ with same-run controls when runner drift matters.
- Keep raw samples and medians; do not certify from one timing sample.
- Separate startup, throughput, memory and output size.
- Retain an optimization only when representative workloads show a real win.