optimize-benchmarks
Iterative performance optimisation loop for syncpack-specifier. Runs benchmarks, identifies bottlenecks, applies optimisations, verifies tests pass and benchmarks improve, then repeats.
Works with
Agent Skills format with YAML frontmatter. Claude Code reads it as-is.
---
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."
license: "MIT"
---
# 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 endMore General & Other skills
find-skills
vercel-labs/skills
Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
grill-me
mattpocock/skills
A relentless interview to sharpen a plan or design.
grill-with-docs
mattpocock/skills
A relentless interview to sharpen a plan or design, which also creates docs (ADR's and glossary) as we go.

