agentsclimarketplace

Performance audit standard

Skill 0xMassi/claude-skills/performance-audit-standard

Twelve Claude Code skills enforcing strict code quality, security, and modern language patterns across TypeScript, Rust, Swift, Go, JavaScript, and PostgreSQL.

Install
npx -y skills add 0xMassi/claude-skills --skill performance-audit-standard

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Performance audit methodology for codebases. Use when profiling applications, identifying bottlenecks, or optimizing hot paths. Covers Big O analysis, hot path identification, data structure optimization (Set/Map over Array), algorithm improvement patterns, async I/O fixes, connection pooling, caching strategies, and priority quick-wins matrix format. Derived from production performance optimization work.

SKILL.md

8.6 KB, as published. Nobody here has run it

Performance Audit Standard

Methodology for identifying and fixing performance bottlenecks.

Audit Process

Step 1: Identify Hot Paths

Find code that runs frequently or processes large datasets:

  • Request handlers (every HTTP request)
  • Event processors (every WebSocket message)
  • Loop bodies processing collections (per-item)
  • Interval timers (every N seconds)
  • Middleware/interceptors (every request)

Ask: "How many times per second does this code execute?" and "What's the typical input size?"

Step 2: Analyze Complexity

For each hot path, determine actual Big O:

PatternComplexityExample
array.includes(x)O(n)Linear scan per check
array.find(x => ...)O(n)Linear scan
array.filter().map().filter()O(3n) + 3 allocationsMultiple passes
Object.entries().find()O(n)Linear scan of object
array.sort() to find min/maxO(n log n)Overkill for single value
Nested loops with includesO(n*m)Quadratic

Step 3: Apply Fixes

Common Performance Anti-Patterns

1. O(n) Membership Test → Use Set

// BAD: O(n) per check, O(n*m) in a loop
const newIds = currentIds.filter(x => !savedIds.includes(x));

// GOOD: O(1) per check, O(n+m) total
const savedSet = new Set(savedIds);
const newIds = currentIds.filter(x => !savedSet.has(x));

Impact: 1000x on large collections (1000 items: 1M comparisons → 1K).

2. O(n log n) Selection → Single-Pass

// BAD: filter + sort + take first = O(n) + O(n log n) + O(1)
const available = tokens.filter(t => !t.expired);
available.sort((a, b) => a.lastUsed - b.lastUsed);
const best = available[0];

// GOOD: Single O(n) pass
let best = null;
for (const t of tokens) {
  if (t.expired) continue;
  if (!best || t.lastUsed < best.lastUsed) best = t;
}

Impact: 10-50x faster, zero intermediate arrays.

3. Linear Lookup → Map Index

// BAD: O(n) per lookup
function findToken(value) {
  return tokens.find(t => t.value === value);
}

// GOOD: O(1) per lookup
const tokenIndex = new Map(tokens.map(t => [t.value, t]));
function findToken(value) {
  return tokenIndex.get(value);
}

Impact: 10-20x per lookup. Maintain index on add/remove.

4. Per-Request Parsing → Cache on Init

// BAD: Parse every time
function getProxy() {
  const proxies = proxySource.split(/\r?\n/);                    // regex split
  const parts = proxies[random()].split(":");                     // string split
  return `http://${parts[2]}:${parts[3]}@${parts[0]}:${parts[1]}`; // format
}

// GOOD: Parse once, cache formatted URLs
class ProxyPool {
  constructor(source) {
    this.urls = source.split(/\r?\n/).filter(Boolean).map(line => {
      const [h, p, u, pw] = line.split(":");
      return `http://${u}:${pw}@${h}:${p}`;
    });
  }
  random() { return this.urls[Math.floor(Math.random() * this.urls.length)]; }
}

Impact: 100-1000x. Regex + string operations are expensive per-call.

5. Sync File I/O → Async

// BAD: Blocks event loop 5-50ms
fs.writeFileSync(path, JSON.stringify(data, null, 2));

// GOOD: Non-blocking
await fs.promises.writeFile(path, JSON.stringify(data, null, 2));

// BETTER: Skip if unchanged
const hash = crypto.createHash("md5").update(JSON.stringify(data)).digest("hex");
if (hash !== this.lastHash) {
  await fs.promises.writeFile(path, JSON.stringify(data, null, 2));
  this.lastHash = hash;
}

6. Per-Request Client Creation → Singleton

// BAD: New TLS handshake + connection pool per call
async fn send(body: Value) {
    let client = reqwest::Client::builder().build().unwrap();
    client.post(url).json(&body).send().await;
}

// GOOD: Build once, reuse forever
static CLIENT: Lazy<reqwest::Client> = Lazy::new(|| {
    reqwest::Client::builder().timeout(Duration::from_secs(10)).build().unwrap()
});

7. Lock Held Across I/O → Clone and Release

// BAD: Lock held during disk write
let mut data = state.data.write().await;
data.insert(key, value);
let json = serde_json::to_string(&*data).unwrap();
std::fs::write(path, json); // DISK I/O WHILE LOCKED

// GOOD: Clone, release, then write
let json = {
    let mut data = state.data.write().await;
    data.insert(key, value);
    serde_json::to_string(&*data).unwrap()
}; // Lock dropped here
tokio::fs::write(path, json).await;

8. Repeated Computation in Loops → Hoist

// BAD: toLowerCase() called 1000x per event
for (const seat of seats) {
  const name = seat.section.toLowerCase();
  const match = filters.find(f => name.includes(f.keyword.toLowerCase()));
}

// GOOD: Pre-compute outside loop
const lowerKeywords = filters.map(f => ({
  ...f,
  lowerKeyword: f.keyword.toLowerCase(),
}));
for (const seat of seats) {
  const name = seat.section.toLowerCase();
  const match = lowerKeywords.find(f => name.includes(f.lowerKeyword));
}

9. Multiple Array Passes → Single Pass

// BAD: 3 passes, 3 intermediate arrays
const resale = picks.filter(p => p.resaleId);
const normal = picks.filter(p => !p.resaleId && !isFiltered(p));
const combined = [...resale.map(transform), ...normal.map(transform)];

// GOOD: Single pass
const results = [];
for (const p of picks) {
  if (p.resaleId) {
    results.push(transformResale(p));
  } else if (!isFiltered(p)) {
    results.push(transformNormal(p));
  }
}

10. Database: N+1 Queries

// BAD: 1 + N queries (1 list, N detail loads)
const users = await db.user.findMany();
for (const u of users) {
  u.orders = await db.order.findMany({ where: { userId: u.id } });
}

// GOOD: 2 queries, joined in app
const users = await db.user.findMany({ include: { orders: true } });

How to find them: log queries per request, set a budget, fail tests if exceeded. Most ORMs have an include / with / Preload to fetch in one round trip.

11. Database: Missing Indexes

EXPLAIN ANALYZE SELECT * FROM events WHERE user_id = $1 ORDER BY created_at DESC LIMIT 50;
-- Seq Scan ... rows=2_500_000  → missing index
CREATE INDEX events_user_id_created_at_idx ON events (user_id, created_at DESC);

Audit: every column in WHERE, JOIN, or ORDER BY on a hot query needs an index. Composite index order matches query order (equality columns first, then range).

12. Frontend: Bundle Size and Core Web Vitals

MetricGoodTool
LCP (Largest Contentful Paint)< 2.5sLighthouse, web-vitals
INP (Interaction to Next Paint)< 200msweb-vitals
CLS (Cumulative Layout Shift)< 0.1web-vitals
JS bundle (gzipped, initial)< 170KBrollup-plugin-visualizer, source-map-explorer

Common wins: code-split routes, lazy-load below-fold images (loading="lazy"), tree-shake icon libs, swap large polyfills.

Priority Quick-Wins Matrix

Report findings in this format:

PriorityFixLocationEffortSpeedup
1Set-based membershipfile:line2 lines1000x
2Single-pass selectionfile:line15 lines10-50x
3Cache parsed datafile:line20 lines100-1000x
4Index Map for lookupsfile:line10 lines10-20x
5Static/singleton clientfile:line5 linesper-alloc savings

Sort by impact/effort ratio. Estimate combined CPU reduction.

Language-Specific Tools

JavaScript/Node.js

  • node --prof + node --prof-process for V8 profiling
  • clinic.js (doctor, flame, bubbleprof)
  • 0x for flame graphs
  • process.memoryUsage() for heap monitoring

Rust

  • cargo flamegraph for flame graphs
  • criterion for microbenchmarks
  • dhat for heap profiling
  • tokio-console for async task inspection
  • perf (Linux) or Instruments (macOS)

Go

  • pprof (CPU + memory profiles)
  • go test -bench
  • trace for goroutine analysis

Swift/iOS

  • Instruments: Time Profiler, Allocations, Leaks
  • Xcode Memory Graph Debugger
  • os_signpost for custom measurements

When NOT to Optimize

  • Code runs < 100 times/second with < 100 items
  • Optimization adds significant complexity for < 2x gain
  • The bottleneck is I/O bound (network, disk), not CPU
  • Profiling shows the code isn't actually hot
  • "Profile first, optimize second": always measure before and after

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.