performance-optimizations
Profile, benchmark, and optimize CMDx task execution, context handling, and runtime hot paths. Use when the user mentions performance, benchmarking, profiling, memory allocation, optimization, slow execution, or YJIT. Don't use for general refactoring, feature additions, or test-only changes.
Best use case
performance-optimizations is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Profile, benchmark, and optimize CMDx task execution, context handling, and runtime hot paths. Use when the user mentions performance, benchmarking, profiling, memory allocation, optimization, slow execution, or YJIT. Don't use for general refactoring, feature additions, or test-only changes.
Teams using performance-optimizations should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/performance-optimizations/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How performance-optimizations Compares
| Feature / Agent | performance-optimizations | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Profile, benchmark, and optimize CMDx task execution, context handling, and runtime hot paths. Use when the user mentions performance, benchmarking, profiling, memory allocation, optimization, slow execution, or YJIT. Don't use for general refactoring, feature additions, or test-only changes.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Performance Optimization
## Prerequisites
Ensure these tools are available before profiling:
```bash
gem install benchmark-ips memory_profiler ruby-prof
```
## Procedures
**Step 0: Baseline from Siblings**
Inspect **similar code paths** in the repo (e.g. another task class, context builder, or runtime hook) and compare allocation/query patterns. This keeps recommendations aligned with how the rest of CMDx solves the same class of problem and avoids introducing an inconsistent optimization style.
**Step 1: Establish a Baseline**
1. Run `ruby scripts/ips-benchmark.rb` for iterations/second across all scenarios, context construction, and access patterns.
2. Run `ruby scripts/memory-profile.rb [scenario]` for per-file/per-line allocation and retained memory report.
3. Run `ruby scripts/allocation-trace.rb` for per-class allocation counts filtered to CMDx source.
4. Run `ruby scripts/yjit-compare.rb` to measure YJIT speedup delta.
5. Save all outputs to a scratch file for before/after comparison.
All script paths are relative to `.cursor/skills/performance-optimizations/`.
**Step 2: Identify the Bottleneck**
Classify the bottleneck:
- **CPU-bound** (deep call stacks, slow methods) → focus on algorithmic changes, memoization, or YJIT-friendly patterns.
- **Allocation-bound** (high GC pressure, many short-lived objects) → focus on object reuse, frozen constants, and avoiding intermediate collections.
Prioritize bottlenecks by **impact × frequency** — a method adding 0.1ms overhead called 1000× per execution outranks a 5ms method called once.
**Step 3: Classify and Apply the Optimization**
Determine which category the fix falls into — prefer low-risk categories first:
| Category | Risk | Examples |
|---|---|---|
| **Allocation reduction** | Low | Frozen strings, `map!` vs `map`, reuse buffers, `EMPTY_HASH`/`EMPTY_ARRAY` |
| **Algorithmic** | Medium | Hash lookup vs linear scan, early returns, `catch`/`throw` vs exceptions |
| **Concurrency** | High | Thread-safe memoization, parallel task execution |
| **Architectural** | High | Structural change to context/runtime data flow |
Then follow these rules in priority order:
1. **Reuse frozen constants** — use `EMPTY_HASH`, `EMPTY_ARRAY`, `EMPTY_STRING` instead of allocating literals in hot paths. These are defined in `lib/cmdx.rb`.
2. **Memoize expensive computations** — use `@foo ||=` for values computed more than once per instance lifetime.
3. **Use `catch`/`throw` for flow control** — this is already the pattern in `Runtime`; never replace it with exceptions for non-error control flow since `throw` is ~10x faster than `raise`.
4. **Estimate impact** before committing — e.g. "reduces allocations ~60%", "eliminates O(n²) lookup". Avoid fake precision; order-of-magnitude or directional estimates are fine.
5. Keep the optimization **isolated** — do not mix performance changes with feature changes in the same commit.
**Step 4: Validate the Change**
1. Re-run `ruby scripts/ips-benchmark.rb` and compare iterations/second against the baseline.
2. Re-run `ruby scripts/memory-profile.rb` and confirm allocated memory/objects decreased (or stayed flat).
3. Re-run `ruby scripts/allocation-trace.rb` and confirm allocation counts decreased (or stayed flat).
4. Run `bundle exec rspec .` to ensure no regressions.
5. Run `bundle exec rubocop .` to ensure style compliance (rubocop-performance cops are active).
6. Verify no regressions in other metrics (e.g. fixing memory shouldn't degrade latency).
**Step 5: Guard Against Regression**
1. Add a spec or benchmark that exercises the optimized path if the bottleneck was severe.
2. Consider **performance budgets** for hot paths — e.g. max allocation count or IPS floor that CI can enforce.
3. If improvement is <5%, reconsider whether the change is worth the added complexity.
**Step 6: Document the Change**
1. Add a `## Performance` entry to `CHANGELOG.md` noting the before/after numbers.
2. If a new frozen constant or memoized value was introduced, add a brief YARD comment explaining the trade-off.
## General Principles
**Measure, don't guess.** Profiling data drives every decision. Intuition about performance is wrong more often than right.
**Optimize the hot path.** Code that runs once during boot doesn't matter. Code in a per-task or per-context-access loop does.
**Reduce allocations before reducing instructions.** In Ruby, GC pressure from object churn often dominates CPU time. Fewer allocations → fewer GC pauses → lower p99 latency.
**Fail fast.** Guard clauses and early returns prevent wasted work. Check the cheapest condition first.
**Prefer lazy over eager (for large datasets).** Use lazy enumerators and streaming when processing unbounded collections.
**Avoid work entirely.** The fastest code is code that doesn't run. Conditional execution and `skip!` guards eliminate unnecessary computation.
**Cache computed results.** If the same computation runs repeatedly with the same inputs, memoize it. Prefer instance-level `@foo ||=` → class-level constant → external cache.
## Anti-Patterns
| Anti-Pattern | Why It Hurts | Fix |
|---|---|---|
| Optimizing without profiling | Wastes time on non-bottlenecks | Profile first, always |
| Micro-optimizing cold paths | No measurable user impact | Focus on hot paths only |
| String concatenation in loops | Creates intermediate string objects | Use `String#<<`, `Array#join`, or `StringIO` |
| Nested loops over collections | O(n²) or worse | Hash lookup, precompute, or restructure |
| Unbounded in-memory collections | RSS spikes, OOM risk, GC stalls | Lazy enumerators, streaming |
| Over-memoizing | Holds references, prevents GC | Memoize only expensive computations |
## Key Patterns
### Frozen Constant Reuse
```ruby
# lib/cmdx.rb already defines these — use them as defaults
EMPTY_ARRAY = [].freeze
EMPTY_HASH = {}.freeze
EMPTY_STRING = ""
```
## YJIT Considerations
- Keep methods short and monomorphic — YJIT inlines small methods aggressively.
- Avoid `method_missing` fan-out on the critical path; YJIT cannot optimize dynamic dispatch.
- `freeze` on value objects helps YJIT prove immutability and skip write barriers.
- Run benchmarks with and without YJIT to measure the delta; report both numbers.
## Scripts Reference
| Script | Gem Dependency | Purpose |
|--------|---------------|---------|
| `scripts/ips-benchmark.rb` | `benchmark-ips` | IPS across execution scenarios, context construction, and access patterns with `compare!` |
| `scripts/memory-profile.rb` | `memory_profiler` | Per-file/per-line allocated and retained memory; accepts scenario arg |
| `scripts/allocation-trace.rb` | stdlib (`objspace`) | Per-class allocation counts via ObjectSpace tracing filtered to CMDx |
| `scripts/yjit-compare.rb` | `benchmark-ips` | Side-by-side YJIT on/off runs with speedup ratios (Ruby 3.3+) |
The project also ships `bin/benchmark` (basic IPS) and `bin/profile` (ruby-prof call graph).
## Final Validation
Cross-reference the completed optimization against `references/checklist.md`.
## Error Handling
- If any script fails with `LoadError`, install the missing gem: `gem install benchmark-ips memory_profiler ruby-prof`.
- If `scripts/allocation-trace.rb` reports `0` allocations, the test helpers may be out of sync with the current codebase. Verify `spec/support/helpers/task_builders.rb` defines the referenced builder methods.
- If `scripts/yjit-compare.rb` aborts with "YJIT not available", ensure CRuby 3.1+ built with `--enable-yjit`. Runtime enable requires Ruby 3.3+.
- If benchmark numbers are noisy (>10% variance between runs), increase warmup time or close background processes.
- If profiling shows no clear bottleneck, check whether the system is I/O-bound — external service latency, file reads, or network round trips.
- If an optimization introduces test failures, it changed behavior — revert and re-profile.
- If memory improves but latency worsens (or vice versa), evaluate whether the trade-off is acceptable for the workload.Related Skills
cmdx
Build, debug, and document CMDx tasks and workflows in Ruby. Use when creating service/command objects with CMDx, composing tasks into workflows, handling halts and faults, or wiring inputs, outputs, callbacks, middleware, retries, and configuration. Don't use for generic Ruby refactors, Rails controller work, or non-CMDx service objects.
test-patterns
Write, structure, and maintain RSpec specs for CMDx tasks, workflows, context, and configuration. Use when the user asks to add, update, fix, or refactor tests, write specs for new features, scaffold test files, or follow project testing conventions. Don't use for debugging production bugs, performance benchmarking, or non-test code changes.
technical-docs
Write, update, and maintain YARD documentation and CHANGELOG entries for CMDx classes, modules, and methods. Use when the user asks to document, add YARD docs, update docs, write docstrings, add @param/@return tags, update CHANGELOG, or fix documentation inconsistencies. Don't use for README generation, non-agentic library docs, or code-only changes.
skill-creator
Authors and structures professional-grade agent skills following the agentskills.io spec. Use when creating new skill directories, drafting procedural instructions, or optimizing metadata for discoverability. Don't use for general documentation, non-agentic library code, or README files.
issue-debugging
Systematically diagnose and resolve bugs, errors, and unexpected behavior in CMDx tasks, workflows, context, and runtime execution. Use when the user mentions a bug, error, unexpected result, failing test, exception, stack trace, wrong state, wrong status, nil value, or debugging. Don't use for feature additions, performance tuning, or test-only changes.
explain-functionality
Explains selected code in depth — data flow, dependencies, side effects, and intent. Use when the user asks to explain, trace, walk through, or understand a function, method, class, module, or code block. Emphasizes how and why over what. Don't use for debugging, refactoring, performance tuning, or generating new code.
command-patterns
Authors and structures Cursor slash-commands that automate recurring developer and AI-maintenance workflows. Use when creating new command files under .cursor/commands/, adding procedural steps, defining constraints, or optimizing command output tables. Do not use for skill authoring, agent definitions, hook scripts, or application code.
typescript-performance
Comprehensive TypeScript performance analysis and optimization. Use when users report slow TypeScript compilation, slow editor experience, or want to investigate TypeScript performance issues. This skill provides diagnostic tools, trace analysis workflows, and optimization strategies. Key triggers include mentions of slow builds, type-checking delays, tsc performance, editor lag with TypeScript, or requests to analyze/improve TypeScript performance. Always prioritize data-driven analysis using --extendedDiagnostics, --generateTrace, and TS Server logs before making optimization recommendations.
golang-performance
Golang performance optimization patterns and methodology - if X bottleneck, then apply Y. Covers allocation reduction, CPU efficiency, memory layout, GC tuning, pooling, caching, and hot-path optimization. Use when profiling or benchmarks have identified a bottleneck and you need the right optimization pattern to fix it. Also use when performing performance code review to suggest improvements or benchmarks that could help identify quick performance gains. Not for measurement methodology (see golang-benchmark skill) or debugging workflow (see golang-troubleshooting skill).
performance-optimizer
Identifies and resolves performance bottlenecks across frontend (Core Web Vitals, code splitting, lazy loading), backend (N+1 queries, async processing), and database (EXPLAIN ANALYZE, indexing strategies) layers. Use when the user reports slow code, latency issues, memory leaks, needs to speed up an application, or wants to benchmark and profile performance.
go-performance
Measure and improve Go program performance on modern Go (1.24+). Use when profiling Go code, diagnosing CPU or memory bottlenecks, investigating latency or contention, writing or fixing benchmarks, comparing benchmark results, using pprof or trace data, applying PGO, or tuning hot-path Go code.
performance-testing-standards
Performance testing standards for load, stress, spike, and soak testing. Covers k6 and JMeter implementation, SLI/SLO definitions, CI/CD integration, and bottleneck analysis. Use when validating system behavior under load, establishing baselines, or capacity planning.