Skip to main content

optimize-benchmarks

Iterative performance optimisation loop for syncpack-specifier. Runs benchmarks, identifies bottlenecks, applies optimisations, verifies tests pass and benchmarks improve, then repeats.

معلومات المصدر

المستودع
JamieMason/syncpack
آخر نشاط في المصدر
٢١ مارس ٢٠٢٦ في ٢٠:٤٠
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٢٬١٠٩
التفرعات
٧٥

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
optimize-benchmarks
description
Iterative performance optimisation loop for syncpack-specifier. Runs benchmarks, identifies bottlenecks, applies optimisations, verifies tests pass and benchmarks improve, then repeats.
# Optimize Benchmarks Iterative loop: benchmark, optimise, test, verify improvement, repeat. ## Workflow ### 1. Baseline ```bash cargo bench -p syncpack-specifier -- --save-baseline before 2>&1 | tail -40 ``` Save the output. Identify which variants are slowest. ### 2. Identify Bottleneck From the full baseline, focus on the **slowest** benchmarks first. Priority order for specifier parsing: 1. `Specifier::create` — the main parse function, called for every version string 2. `parser::is_range` — checks 12 regexes sequentially 3. `parser::is_exact` — checks 4 regexes sequentially 4. `parser::is_complex_range` — splits, collects, iterates 5. Individual regex matches in `regexes.rs` ### 3. Apply ONE Optimisation Make a **single, focused change**. Do NOT bundle multiple optimisations — each must be independently measurable. ### 4. Verify Tests Pass ```bash cargo test -p syncpack-specifier 2>&1 | tail -5 ``` If tests fail, fix or revert. Never proceed with failing tests. ### 5. Benchmark Against Baseline ```bash cargo bench -p syncpack-specifier -- --baseline before 2>&1 | tail -40 ``` Look for `[-XX.XXX% ...]` (improvement) or `[+XX.XXX% ...]` (regression). ### 6. Evaluate - **Improved:** Report the gains. Update baseline: `cargo bench -p syncpack-specifier -- --save-baseline before`. Continue to step 2. - **No change:** Revert and try a different approach. - **Regressed:** Revert immediately. ### 7. Repeat Go to step 2. Stop when: - User says stop - No bottlenecks remain - Gains are <1% across all benchmarks ## Known Optimisation Opportunities ### High Impact **Replace regex with char-based parsing in `parser.rs` / `regexes.rs`** Most regexes in `regexes.rs` match simple patterns like `^[0-9]+\.[0-9]+\.[0-9]+$` (exact semver). These can be replaced with byte/char iteration: ```rust // Instead of regex EXACT: r"^[0-9]+\.[0-9]+\.[0-9]+$" fn is_exact_version(s: &str) -> bool { let mut dots = 0; let bytes = s.as_bytes(); if bytes.is_empty() { return false; } for &b in bytes { match b { b'0'..=b'9' => {}, b'.' => dots += 1, _ => return false, } } dots == 2 } ``` Regex `is_match()` has overhead even for simple patterns: engine setup, capture group allocation. Char-based parsing for these patterns is 5-20x faster. **Reduce sequential regex attempts in `parser::is_range`** `is_range` tries 12 regexes. Instead, match on first char(s) to dispatch: ```rust fn is_range(s: &str) -> bool { match s.as_bytes().first() { Some(b'^') => is_semver_after(s, 1) || is_semver_tag_after(s, 1), Some(b'~') => is_semver_after(s, 1) || is_semver_tag_after(s, 1), Some(b'>') => { /* check >= vs > then validate remainder */ }, Some(b'<') => { /* check <= vs < then validate remainder */ }, _ => false, } } ``` **Consolidate related regex patterns** Many regexes are pairs: `EXACT` + `EXACT_TAG`, `CARET` + `CARET_TAG`, etc. Merge each pair into one function that handles both cases: ```rust fn is_exact(s: &str) -> bool { // Parse digits.digits.digits, then optionally -tag let rest = parse_semver_triple(s)?; rest.is_empty() || rest.starts_with('-') } ``` ### Medium Impact **Replace `lazy_static` with `std::sync::OnceLock`** `lazy_static` uses an extra indirection layer. `OnceLock` (stable since Rust 1.80) is zero-cost after init: ```rust use std::sync::OnceLock; fn exact_regex() -> &'static Regex { static RE: OnceLock<Regex> = OnceLock::new(); RE.get_or_init(|| Regex::new(r"^[0-9]+\.[0-9]+\.[0-9]+$").unwrap()) } ``` But if regex is being replaced with char-based parsing, this becomes irrelevant. **Reorder checks in `Specifier::create` by frequency** In a typical monorepo, most specifiers are `^x.y.z` (range) or `x.y.z` (exact). The current order already checks exact first, then range — good. But `is_exact` tries 4 regex patterns. A single fast char check can short-circuit: ```rust // Fast path: first char is digit → likely exact or major or minor // Fast path: first char is ^ or ~ → likely range ``` **Avoid `String` allocation in `strip_semver_range`** `strip_semver_range` returns `&str` (already good), but callers like `Range::create` then `.to_string()` the result. Consider whether the allocation can be deferred. ### Low Impact - Replace `HashMap` in caches with `FxHashMap` (faster hashing for short strings) - Use `SmallString` or stack-allocated strings for short specifiers - Pre-size cache `HashMap` with expected capacity ## Architecture Notes Key files in `crates/syncpack-specifier/src/`: | File | Role | | ---------------------------- | ----------------------------------------------------------- | | `lib.rs` | `Specifier` enum, `create()` dispatch, caches | | `parser.rs` | `is_exact()`, `is_range()`, etc. — classification functions | | `regexes.rs` | All `lazy_static` regex patterns | | `exact.rs`, `range.rs`, etc. | Variant constructors calling `node_semver` | | `semver_range.rs` | `SemverRange` enum, `parse()` | The hot path is: `Specifier::create()` → `parser::is_*()` → `regexes::*` → variant `::create()` → `node_semver` parsing. Optimising the `parser::is_*` layer gives the biggest wins because it runs for **every** specifier, and most of the time most checks return `false` (only one branch matches). ## Rules - ONE change per iteration - Always verify tests pass before benchmarking - Always compare against baseline - Report numbers: `before → after (% change)` - Revert regressions immediately - Don't optimise what doesn't show up in benchmarks - Use **fast iteration** (`"batch"` filter) during the loop, **full suite** only at start and end
عرض على GitHub