Performance Optimization
Performance work follows one rule above all others: measure before you change anything. Intuition about bottlenecks is wrong more often than it is right. Every optimization should start with profiling, produce a hypothesis, apply a targeted fix, and verify with another measurement.
Core Principles
| Principle |
Meaning |
| Measure first |
Never optimize without profiling data - gut feelings about bottlenecks are unreliable |
| Optimize the critical path |
Focus on the code that runs most frequently or blocks user-visible latency |
| Set budgets |
Define acceptable latency, throughput, and resource usage before you start |
| Avoid premature optimization |
Readable, correct code first - optimize only when measurements show a real problem |
| Know your tradeoffs |
Every optimization trades something (memory for speed, complexity for throughput, freshness for latency) |
Profiling and Benchmarking
Profiling identifies where time and resources are spent. Without it, you are guessing.
Types of Profiling
| Type |
What It Reveals |
When to Use |
| CPU profiling |
Hot functions, call frequency, execution time distribution |
Slow request handling, high CPU usage |
| Memory profiling |
Allocation rates, heap size, object retention, leaks |
Growing memory usage, OOM errors, GC pressure |
| I/O profiling |
Disk reads/writes, network calls, blocking waits |
Slow file operations, external service latency |
| Database profiling |
Query execution time, query count per request, slow queries |
High DB load, N+1 patterns, missing indexes |
The Profiling Workflow
- Baseline - Capture metrics under normal conditions before any changes
- Identify - Find the hotspot consuming the most time or resources
- Hypothesize - Form a specific theory about why it is slow
- Fix - Apply a single, targeted change
- Verify - Measure again to confirm improvement and check for regressions
Performance Budgets
Define limits that trigger action when exceeded:
- Response time: P50, P95, P99 latency targets per endpoint
- Throughput: Minimum requests per second under expected load
- Resource usage: CPU, memory, and connection limits per service
- Page weight: Maximum transfer size for frontend assets
See Profiling Patterns Reference for detailed profiling workflows, bottleneck signatures, and load testing strategies.
Caching Strategies
Caching eliminates redundant computation and data fetching by storing results closer to where they are needed.
Cache Layers
| Layer |
Location |
Latency |
Use Case |
| L1 - In-process |
Application memory (object cache, memoization) |
Nanoseconds |
Hot data accessed many times per request |
| L2 - Distributed |
Redis, Memcached, shared cache |
Sub-millisecond to low milliseconds |
Data shared across application instances |
| HTTP cache |
Browser, reverse proxy (Varnish, Nginx) |
Zero network round-trip for client cache |
Static assets, cacheable API responses |
| CDN |
Edge servers worldwide |
Low latency from geographic proximity |
Static files, pre-rendered pages, media |
| Database cache |
Query result cache, buffer pool |
Varies |
Repeated identical queries |
Invalidation Approaches
| Strategy |
How It Works |
Best For |
| TTL-based |
Cache entries expire after a fixed duration |
Data that tolerates bounded staleness |
| Event-based |
Cache is cleared when the source data changes |
Data that must stay fresh after writes |
| Write-through |
Writes update both the cache and the backing store simultaneously |
Read-heavy workloads needing strong consistency |
| Write-behind |
Writes update the cache immediately; backing store is updated asynchronously |
High write throughput where eventual consistency is acceptable |
Cache Stampede Prevention
When a popular cache key expires, many concurrent requests may all try to regenerate it at once, overwhelming the backend. Three approaches prevent this:
- Locking - Only one request regenerates; others wait or serve stale data
- Probabilistic early recomputation - Requests randomly refresh the cache before expiration, spreading regeneration over time
- Request coalescing - Duplicate in-flight requests are collapsed into a single backend call
See Caching Strategies Reference for implementation patterns with multi-language examples.
Database Optimization
Database queries are the most common performance bottleneck in web applications.
Index Strategy
- Create indexes on columns used in WHERE, JOIN, and ORDER BY clauses
- Use composite indexes that match your most frequent query patterns (leftmost prefix rule)
- Covering indexes include all columns a query needs, avoiding table lookups entirely
- Monitor unused indexes - they slow down writes without helping reads
N+1 Query Prevention
The N+1 problem occurs when code fetches a list of N records, then issues one additional query per record to load related data. Instead of 1 query, you execute N+1.
Detection signals:
- Query count scales linearly with result set size
- Many nearly identical queries differing only in a single parameter
- Profiler shows dozens or hundreds of queries for a single page load
Prevention strategies:
- Eager loading (JOIN or separate batch query upfront)
- Batch loading (collect IDs, fetch all related records in one query)
- DataLoader pattern (automatic batching and deduplication within a request)
Connection Pooling
Opening a database connection is expensive (TCP handshake, authentication, TLS negotiation). Connection pools maintain a set of reusable connections:
- Size the pool based on expected concurrency - too small causes queueing, too large overwhelms the database
- Always return connections to the pool promptly - leaked connections exhaust the pool
- Set idle timeouts to reclaim unused connections
- Use external poolers (like PgBouncer for PostgreSQL) when application-level pooling is insufficient
See Database Optimization Reference for query patterns, explain plan analysis, and multi-language examples.
Memory and Resource Management
Memory Optimization Patterns
| Pattern |
Description |
| Object pooling |
Reuse expensive objects instead of allocating and discarding them |
| Streaming |
Process large datasets as streams instead of loading everything into memory |
| Lazy initialization |
Defer creation of expensive objects until they are actually needed |
| Weak references |
Hold references that do not prevent garbage collection |
| Buffer reuse |
Allocate buffers once and reuse them across operations |
Lazy Loading
Lazy loading defers work until the result is actually needed. It reduces startup time and memory usage but adds complexity and can cause unexpected latency later.
Where lazy loading helps:
- Loading related database records only when accessed
- Initializing expensive service connections on first use
- Loading UI components or assets only when they become visible
Where lazy loading hurts:
- When the deferred work always happens anyway (just adds overhead)
- When it moves latency from a predictable startup phase to unpredictable user interactions
- When it creates N+1 query patterns (see Database Optimization above)
Batch Operations
Replace individual operations with batch alternatives wherever possible:
- Batch inserts instead of inserting one row at a time
- Batch API calls instead of calling an external service N times
- Bulk file operations instead of processing files individually
Quick Reference: Common Bottleneck Patterns
| Symptom |
Likely Cause |
First Investigation Step |
| Slow response times, low CPU |
I/O waits (database, network, disk) |
Profile I/O and check query logs |
| High CPU, normal response times |
Inefficient algorithms or excessive computation |
CPU profile to find hot functions |
| Growing memory over time |
Memory leak (unreleased references, unbounded caches) |
Heap dump comparison over time |
| Intermittent slowness under load |
Resource contention (locks, connection pool exhaustion) |
Check pool sizes and lock wait times |
| Fast locally, slow in production |
Network latency, missing caches, different data volumes |
Compare profiling data between environments |
Reference Files
| Reference |
Contents |
| Caching Strategies |
Cache layers, invalidation patterns, stampede prevention with multi-language examples |
| Database Optimization |
Query optimization, N+1 prevention, connection pooling, batch operations with multi-language examples |
| Profiling Patterns |
Profiling workflows, bottleneck signatures, performance budgets, load testing strategies |
Integration with Other Skills
| Situation |
Recommended Skill |
| Performance issues caused by poor architecture |
Install knowledge-virtuoso from krzysztofsurdy/code-virtuoso for clean architecture guidance |
| Need to refactor slow code paths |
Install knowledge-virtuoso from krzysztofsurdy/code-virtuoso for refactoring techniques |
| API response time optimization |
Install knowledge-virtuoso from krzysztofsurdy/code-virtuoso for API design principles |
| Database schema and query design |
Install knowledge-virtuoso from krzysztofsurdy/code-virtuoso for testing strategies to verify optimizations |
1---2name: performance3description: Application performance optimization patterns and profiling-driven methodology. Use when the user asks to optimize application speed, reduce latency, diagnose slow queries, fix N+1 problems, implement caching layers, profile memory usage, tune database queries, apply lazy loading, configure connection pooling, or set performance budgets. Covers CPU and memory profiling, caching strategies (application, HTTP, CDN), query optimization, indexing, and load testing approaches.4---56# Performance Optimization78Performance work follows one rule above all others: measure before you change anything. Intuition about bottlenecks is wrong more often than it is right. Every optimization should start with profiling, produce a hypothesis, apply a targeted fix, and verify with another measurement.910## Core Principles1112| Principle | Meaning |13|---|---|14| **Measure first** | Never optimize without profiling data - gut feelings about bottlenecks are unreliable |15| **Optimize the critical path** | Focus on the code that runs most frequently or blocks user-visible latency |16| **Set budgets** | Define acceptable latency, throughput, and resource usage before you start |17| **Avoid premature optimization** | Readable, correct code first - optimize only when measurements show a real problem |18| **Know your tradeoffs** | Every optimization trades something (memory for speed, complexity for throughput, freshness for latency) |1920---2122## Profiling and Benchmarking2324Profiling identifies where time and resources are spent. Without it, you are guessing.2526### Types of Profiling2728| Type | What It Reveals | When to Use |29|---|---|---|30| **CPU profiling** | Hot functions, call frequency, execution time distribution | Slow request handling, high CPU usage |31| **Memory profiling** | Allocation rates, heap size, object retention, leaks | Growing memory usage, OOM errors, GC pressure |32| **I/O profiling** | Disk reads/writes, network calls, blocking waits | Slow file operations, external service latency |33| **Database profiling** | Query execution time, query count per request, slow queries | High DB load, N+1 patterns, missing indexes |3435### The Profiling Workflow36371. **Baseline** - Capture metrics under normal conditions before any changes382. **Identify** - Find the hotspot consuming the most time or resources393. **Hypothesize** - Form a specific theory about why it is slow404. **Fix** - Apply a single, targeted change415. **Verify** - Measure again to confirm improvement and check for regressions4243### Performance Budgets4445Define limits that trigger action when exceeded:4647- **Response time**: P50, P95, P99 latency targets per endpoint48- **Throughput**: Minimum requests per second under expected load49- **Resource usage**: CPU, memory, and connection limits per service50- **Page weight**: Maximum transfer size for frontend assets5152See [Profiling Patterns Reference](references/profiling-patterns.md) for detailed profiling workflows, bottleneck signatures, and load testing strategies.5354---5556## Caching Strategies5758Caching eliminates redundant computation and data fetching by storing results closer to where they are needed.5960### Cache Layers6162| Layer | Location | Latency | Use Case |63|---|---|---|---|64| **L1 - In-process** | Application memory (object cache, memoization) | Nanoseconds | Hot data accessed many times per request |65| **L2 - Distributed** | Redis, Memcached, shared cache | Sub-millisecond to low milliseconds | Data shared across application instances |66| **HTTP cache** | Browser, reverse proxy (Varnish, Nginx) | Zero network round-trip for client cache | Static assets, cacheable API responses |67| **CDN** | Edge servers worldwide | Low latency from geographic proximity | Static files, pre-rendered pages, media |68| **Database cache** | Query result cache, buffer pool | Varies | Repeated identical queries |6970### Invalidation Approaches7172| Strategy | How It Works | Best For |73|---|---|---|74| **TTL-based** | Cache entries expire after a fixed duration | Data that tolerates bounded staleness |75| **Event-based** | Cache is cleared when the source data changes | Data that must stay fresh after writes |76| **Write-through** | Writes update both the cache and the backing store simultaneously | Read-heavy workloads needing strong consistency |77| **Write-behind** | Writes update the cache immediately; backing store is updated asynchronously | High write throughput where eventual consistency is acceptable |7879### Cache Stampede Prevention8081When a popular cache key expires, many concurrent requests may all try to regenerate it at once, overwhelming the backend. Three approaches prevent this:8283- **Locking** - Only one request regenerates; others wait or serve stale data84- **Probabilistic early recomputation** - Requests randomly refresh the cache before expiration, spreading regeneration over time85- **Request coalescing** - Duplicate in-flight requests are collapsed into a single backend call8687See [Caching Strategies Reference](references/caching-strategies.md) for implementation patterns with multi-language examples.8889---9091## Database Optimization9293Database queries are the most common performance bottleneck in web applications.9495### Index Strategy9697- Create indexes on columns used in WHERE, JOIN, and ORDER BY clauses98- Use composite indexes that match your most frequent query patterns (leftmost prefix rule)99- Covering indexes include all columns a query needs, avoiding table lookups entirely100- Monitor unused indexes - they slow down writes without helping reads101102### N+1 Query Prevention103104The N+1 problem occurs when code fetches a list of N records, then issues one additional query per record to load related data. Instead of 1 query, you execute N+1.105106**Detection signals:**107- Query count scales linearly with result set size108- Many nearly identical queries differing only in a single parameter109- Profiler shows dozens or hundreds of queries for a single page load110111**Prevention strategies:**112- Eager loading (JOIN or separate batch query upfront)113- Batch loading (collect IDs, fetch all related records in one query)114- DataLoader pattern (automatic batching and deduplication within a request)115116### Connection Pooling117118Opening a database connection is expensive (TCP handshake, authentication, TLS negotiation). Connection pools maintain a set of reusable connections:119120- Size the pool based on expected concurrency - too small causes queueing, too large overwhelms the database121- Always return connections to the pool promptly - leaked connections exhaust the pool122- Set idle timeouts to reclaim unused connections123- Use external poolers (like PgBouncer for PostgreSQL) when application-level pooling is insufficient124125See [Database Optimization Reference](references/database-optimization.md) for query patterns, explain plan analysis, and multi-language examples.126127---128129## Memory and Resource Management130131### Memory Optimization Patterns132133| Pattern | Description |134|---|---|135| **Object pooling** | Reuse expensive objects instead of allocating and discarding them |136| **Streaming** | Process large datasets as streams instead of loading everything into memory |137| **Lazy initialization** | Defer creation of expensive objects until they are actually needed |138| **Weak references** | Hold references that do not prevent garbage collection |139| **Buffer reuse** | Allocate buffers once and reuse them across operations |140141### Lazy Loading142143Lazy loading defers work until the result is actually needed. It reduces startup time and memory usage but adds complexity and can cause unexpected latency later.144145**Where lazy loading helps:**146- Loading related database records only when accessed147- Initializing expensive service connections on first use148- Loading UI components or assets only when they become visible149150**Where lazy loading hurts:**151- When the deferred work always happens anyway (just adds overhead)152- When it moves latency from a predictable startup phase to unpredictable user interactions153- When it creates N+1 query patterns (see Database Optimization above)154155### Batch Operations156157Replace individual operations with batch alternatives wherever possible:158159- Batch inserts instead of inserting one row at a time160- Batch API calls instead of calling an external service N times161- Bulk file operations instead of processing files individually162163---164165## Quick Reference: Common Bottleneck Patterns166167| Symptom | Likely Cause | First Investigation Step |168|---|---|---|169| Slow response times, low CPU | I/O waits (database, network, disk) | Profile I/O and check query logs |170| High CPU, normal response times | Inefficient algorithms or excessive computation | CPU profile to find hot functions |171| Growing memory over time | Memory leak (unreleased references, unbounded caches) | Heap dump comparison over time |172| Intermittent slowness under load | Resource contention (locks, connection pool exhaustion) | Check pool sizes and lock wait times |173| Fast locally, slow in production | Network latency, missing caches, different data volumes | Compare profiling data between environments |174175---176177## Reference Files178179| Reference | Contents |180|---|---|181| [Caching Strategies](references/caching-strategies.md) | Cache layers, invalidation patterns, stampede prevention with multi-language examples |182| [Database Optimization](references/database-optimization.md) | Query optimization, N+1 prevention, connection pooling, batch operations with multi-language examples |183| [Profiling Patterns](references/profiling-patterns.md) | Profiling workflows, bottleneck signatures, performance budgets, load testing strategies |184185---186187## Integration with Other Skills188189| Situation | Recommended Skill |190|---|---|191| Performance issues caused by poor architecture | Install `knowledge-virtuoso` from `krzysztofsurdy/code-virtuoso` for clean architecture guidance |192| Need to refactor slow code paths | Install `knowledge-virtuoso` from `krzysztofsurdy/code-virtuoso` for refactoring techniques |193| API response time optimization | Install `knowledge-virtuoso` from `krzysztofsurdy/code-virtuoso` for API design principles |194| Database schema and query design | Install `knowledge-virtuoso` from `krzysztofsurdy/code-virtuoso` for testing strategies to verify optimizations |