Code optimizer
Skill yigityildiz0/universal-ai-skill-library/skills/common/code-optimizer
Performance-focused code optimization covering algorithmic complexity, memory usage, I/O efficiency, caching, and concurrency. Use when code is slow, uses.From its SKILL.md
npx -y skills add yigityildiz0/universal-ai-skill-library --skill code-optimizerAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
3 things to look at
- 21 days oldThe repository was created 21 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 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.
SKILL.md
20.6 KB, ~4.7k tokens by cl100k_base, as published. Nobody here has run it
Code Optimizer
Systematic performance optimization of code through profiling, algorithmic improvement, memory optimization, I/O efficiency, caching strategies, and concurrency patterns. This skill emphasizes measurement-driven optimization, targeting verified bottlenecks rather than applying premature optimizations.
When to Use This Skill
Use this skill for:
- Code that is demonstrably too slow for its performance requirements
- Functions or endpoints that have been identified as bottlenecks through profiling
- Reducing memory consumption to stay within resource limits
- Optimizing I/O-bound operations (database queries, file processing, network calls)
- Improving response times for user-facing operations
- Reducing cloud infrastructure costs through more efficient resource usage
- Preparing code for higher scale (10x or 100x current load)
- Batch processing jobs that take too long to complete
Trigger phrases: "optimize", "performance", "slow code", "speed up", "bottleneck", "reduce memory", "cache", "too slow", "optimize query", "improve performance", "latency", "throughput"
What This Skill Does
This skill provides a structured optimization methodology:
- Profiling Guidance: Identifies what to measure and how to find the actual bottleneck before optimizing
- Algorithmic Optimization: Reduces time complexity by selecting better algorithms and data structures
- Memory Optimization: Reduces memory footprint through data structure choices, lazy evaluation, and object reuse
- I/O Optimization: Minimizes I/O overhead through batching, connection pooling, and efficient serialization
- Caching Strategies: Applies appropriate caching at the right layer to eliminate redundant computation
- Concurrency Patterns: Leverages parallelism and asynchronous processing for CPU-bound and I/O-bound workloads
- Trade-off Analysis: Evaluates readability, maintainability, and correctness costs of each optimization
Instructions
Step 1: Profile Before Optimizing
Never optimize without data. Identify the actual bottleneck through measurement.
Profiling Tools by Language
| Language | CPU Profiler | Memory Profiler | I/O Profiler |
|---|---|---|---|
| Python | cProfile, py-spy, scalene | tracemalloc, memory_profiler, scalene | strace, py-spy |
| JavaScript | Chrome DevTools, clinic.js | Chrome DevTools Heap Snapshot, memwatch | clinic.js bubbleprof |
| Java | JFR (Java Flight Recorder), async-profiler | jmap, MAT, VisualVM | JFR I/O events, strace |
Python Example: Profile Before Optimizing
import cProfile
import pstats
from io import StringIO
def profile_function(func, *args, **kwargs):
"""Profile a function and print the top 20 hotspots."""
profiler = cProfile.Profile()
profiler.enable()
result = func(*args, **kwargs)
profiler.disable()
stream = StringIO()
stats = pstats.Stats(profiler, stream=stream)
stats.sort_stats("cumulative")
stats.print_stats(20)
print(stream.getvalue())
return result
# Profile the slow function to find the bottleneck
profile_function(process_large_dataset, dataset)
Key rule: the profile output tells you where the time is actually spent. Optimize the top entries, not what you assume is slow.
Step 2: Optimize Algorithms and Data Structures
The highest-impact optimization is reducing algorithmic complexity.
Common Complexity Improvements
| Pattern | Before | After | Speedup (n=10,000) |
|---|---|---|---|
| Linear search to hash lookup | O(n) per lookup | O(1) per lookup | ~10,000x |
| Nested loops to hash join | O(n*m) | O(n+m) | ~10,000x |
| Repeated sorting to sorted insertion | O(n * n log n) | O(n log n) | ~10,000x |
| String concatenation in loop | O(n^2) | O(n) with join/builder | ~5,000x |
| Recomputing results | O(n * f(n)) | O(n) with memoization | Varies |
Python Example: O(n^2) to O(n) with Hash Lookup
# BEFORE: O(n * m) -- nested loop to find matching records
def find_matching_orders(orders, customers):
"""Find orders with matching customer records."""
results = []
for order in orders: # O(n)
for customer in customers: # O(m) for each order
if order["customer_id"] == customer["id"]:
results.append({**order, "customer_name": customer["name"]})
break
return results
# Total: O(n * m) -- with 10K orders and 10K customers: 100M comparisons
# AFTER: O(n + m) -- build lookup table first
def find_matching_orders(orders, customers):
"""Find orders with matching customer records."""
customer_map = {c["id"]: c for c in customers} # O(m) -- build once
results = []
for order in orders: # O(n)
customer = customer_map.get(order["customer_id"]) # O(1) lookup
if customer:
results.append({**order, "customer_name": customer["name"]})
return results
# Total: O(n + m) -- with 10K orders and 10K customers: 20K operations
JavaScript Example: String Concatenation Optimization
// BEFORE: O(n^2) -- string concatenation creates new string each iteration
function buildHtmlTable(rows) {
let html = "<table>";
for (const row of rows) {
html += "<tr>"; // Each += copies the entire string
for (const cell of row) {
html += `<td>${cell}</td>`;
}
html += "</tr>";
}
html += "</table>";
return html;
}
// AFTER: O(n) -- array join builds string once
function buildHtmlTable(rows) {
const parts = ["<table>"];
for (const row of rows) {
parts.push("<tr>");
for (const cell of row) {
parts.push(`<td>${cell}</td>`);
}
parts.push("</tr>");
}
parts.push("</table>");
return parts.join("");
}
Java Example: Collection Choice Optimization
// BEFORE: Using ArrayList for frequent contains() checks -- O(n) per check
List<String> processedIds = new ArrayList<>();
for (Event event : events) {
if (!processedIds.contains(event.getId())) { // O(n) scan each time
processEvent(event);
processedIds.add(event.getId());
}
}
// Total: O(n^2) for n events
// AFTER: Using HashSet for O(1) contains() checks
Set<String> processedIds = new HashSet<>();
for (Event event : events) {
if (processedIds.add(event.getId())) { // O(1) check + add
processEvent(event);
}
}
// Total: O(n) for n events
Step 3: Optimize Memory Usage
Reduce memory consumption when working with large datasets or resource-constrained environments.
Memory Optimization Techniques
| Technique | When to Use | Typical Savings |
|---|---|---|
| Generators / Iterators | Processing large sequences one element at a time | Memory proportional to one element vs. entire sequence |
| Streaming I/O | Reading large files | Constant memory vs. file-size memory |
| Object pooling | Frequent creation/destruction of expensive objects | Reduces GC pressure |
| Data structure compaction | Storing large numbers of small objects | 2-10x with slots, structs, typed arrays |
| Lazy evaluation | Computing values that may not be needed | Avoids unnecessary computation and allocation |
| Weak references | Caching objects that can be recreated | Prevents memory leaks in caches |
Python Example: Generator for Memory Efficiency
# BEFORE: Loads entire dataset into memory
def process_large_csv(filepath):
with open(filepath) as f:
reader = csv.DictReader(f)
rows = list(reader) # Loads ALL rows into memory
results = []
for row in rows:
if float(row["amount"]) > 1000:
results.append(transform(row))
return results
# Memory: O(n) for n rows -- 10M rows = several GB
# AFTER: Generator processes one row at a time
def process_large_csv(filepath):
def row_generator():
with open(filepath) as f:
reader = csv.DictReader(f)
for row in reader: # Yields one row at a time
if float(row["amount"]) > 1000:
yield transform(row)
return row_generator()
# Memory: O(1) -- constant regardless of file size
Java Example: Memory-Efficient Data Structure
// BEFORE: Each Point object has 16 bytes overhead (object header) + 16 bytes data
// For 10M points: ~320 MB
List<Point> points = new ArrayList<>();
for (int i = 0; i < 10_000_000; i++) {
points.add(new Point(xValues[i], yValues[i]));
}
// AFTER: Parallel arrays eliminate per-object overhead
// For 10M points: ~80 MB (just the raw doubles)
double[] xCoords = new double[10_000_000];
double[] yCoords = new double[10_000_000];
System.arraycopy(xValues, 0, xCoords, 0, 10_000_000);
System.arraycopy(yValues, 0, yCoords, 0, 10_000_000);
Step 4: Optimize I/O Operations
I/O is typically the largest bottleneck in real-world applications.
I/O Optimization Strategies
| Strategy | Technique | Impact |
|---|---|---|
| Batching | Combine multiple small I/O operations into fewer large ones | 10-100x for database writes |
| Connection pooling | Reuse connections instead of creating new ones | 5-50x for database/HTTP |
| Async I/O | Overlap I/O operations instead of waiting sequentially | 2-10x for multiple independent I/O |
| Compression | Compress data before network transfer | 2-10x for text-heavy payloads |
| Pagination | Fetch data in pages instead of all at once | Bounded memory, better time-to-first-result |
| Selective loading | Load only needed fields (SELECT specific columns, GraphQL) | 2-5x for wide tables |
Python Example: Batch Database Operations
# BEFORE: Individual inserts -- 10K round trips to the database
def save_records(records, db):
for record in records:
db.execute(
"INSERT INTO events (id, type, data) VALUES (?, ?, ?)",
(record["id"], record["type"], json.dumps(record["data"]))
)
db.commit()
# 10K records = 10K round trips, ~30 seconds
# AFTER: Batch insert -- 1 round trip
def save_records(records, db):
values = [
(record["id"], record["type"], json.dumps(record["data"]))
for record in records
]
db.executemany(
"INSERT INTO events (id, type, data) VALUES (?, ?, ?)",
values
)
db.commit()
# 10K records = 1 round trip, ~0.3 seconds
JavaScript Example: Parallel Async I/O
// BEFORE: Sequential fetches -- total time = sum of all fetch times
async function enrichUserProfiles(userIds) {
const profiles = [];
for (const id of userIds) {
const user = await fetchUser(id); // Wait for each one
const orders = await fetchOrders(id); // Then wait for this
const preferences = await fetchPrefs(id); // Then wait for this
profiles.push({ ...user, orders, preferences });
}
return profiles;
}
// 100 users x 3 sequential calls x 100ms each = 30 seconds
// AFTER: Parallel fetches -- total time = max of all fetch times
async function enrichUserProfiles(userIds) {
const profiles = await Promise.all(
userIds.map(async (id) => {
// All three fetches for each user run in parallel
const [user, orders, preferences] = await Promise.all([
fetchUser(id),
fetchOrders(id),
fetchPrefs(id),
]);
return { ...user, orders, preferences };
})
);
return profiles;
}
// 100 users x 1 parallel batch x 100ms = ~1 second (with connection pool)
Step 5: Implement Caching
Caching eliminates redundant computation and I/O by storing and reusing results.
Caching Strategy Selection
| Cache Type | Use When | Invalidation Strategy |
|---|---|---|
| In-memory (function-level) | Pure function called repeatedly with same inputs | LRU eviction, TTL |
| Application-level | Expensive computation shared across requests | TTL, event-based invalidation |
| Distributed (Redis, Memcached) | Shared state across multiple server instances | TTL, explicit invalidation |
| HTTP caching | API responses or static assets | ETag, Cache-Control headers |
| Query result cache | Expensive database queries with stable results | TTL, write-through invalidation |
Python Example: Memoization with LRU Cache
from functools import lru_cache
import time
# BEFORE: Recomputes expensive result every call
def get_exchange_rate(from_currency, to_currency):
# Calls external API -- 200ms per call
response = requests.get(f"https://api.rates.com/{from_currency}/{to_currency}")
return response.json()["rate"]
def convert_prices(products, target_currency):
for product in products:
rate = get_exchange_rate(product.currency, target_currency) # API call each time
product.converted_price = product.price * rate
# 1000 products with 5 source currencies = 1000 API calls (but only 5 unique)
# AFTER: Cache exchange rates (TTL via maxsize, refresh externally)
@lru_cache(maxsize=128)
def get_exchange_rate(from_currency, to_currency):
response = requests.get(f"https://api.rates.com/{from_currency}/{to_currency}")
return response.json()["rate"]
def convert_prices(products, target_currency):
for product in products:
rate = get_exchange_rate(product.currency, target_currency) # Cached after first call
product.converted_price = product.price * rate
# 1000 products with 5 source currencies = 5 API calls (cache hits for rest)
Java Example: Application-Level Cache
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.time.Duration;
public class ProductService {
private final Cache<String, ProductDetails> productCache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(Duration.ofMinutes(5))
.recordStats()
.build();
public ProductDetails getProductDetails(String productId) {
return productCache.get(productId, this::loadProductDetails);
}
private ProductDetails loadProductDetails(String productId) {
// Expensive: joins 4 tables, calls pricing service, fetches inventory
Product product = productRepository.findById(productId);
Pricing pricing = pricingService.getPrice(productId);
Inventory inventory = inventoryService.getStock(productId);
return new ProductDetails(product, pricing, inventory);
}
// Invalidate on write
public void updateProduct(String productId, ProductUpdate update) {
productRepository.save(update);
productCache.invalidate(productId);
}
}
Step 6: Leverage Concurrency
Use parallelism for CPU-bound work and async I/O for I/O-bound work.
Concurrency Decision Matrix
| Workload Type | Single-Threaded Bottleneck | Solution |
|---|---|---|
| CPU-bound, independent tasks | One core idle per task | Multi-processing / thread pool |
| I/O-bound, multiple sources | Waiting for one I/O blocks others | Async I/O / event loop |
| Mixed CPU + I/O | Either CPU or I/O blocks | Async I/O for I/O, thread pool for CPU |
| Pipeline processing | Each stage waits for previous | Pipeline parallelism (producer-consumer) |
Python Example: Parallel Processing with ProcessPoolExecutor
from concurrent.futures import ProcessPoolExecutor, as_completed
import multiprocessing
def analyze_file(filepath):
"""CPU-intensive analysis of a single file."""
with open(filepath) as f:
content = f.read()
# Expensive computation: parsing, analysis, etc.
return {"file": filepath, "lines": content.count("\n"), "score": compute_score(content)}
# BEFORE: Sequential processing
def analyze_all_files(filepaths):
return [analyze_file(fp) for fp in filepaths]
# 1000 files x 500ms each = 500 seconds
# AFTER: Parallel processing across CPU cores
def analyze_all_files(filepaths):
results = []
num_workers = multiprocessing.cpu_count()
with ProcessPoolExecutor(max_workers=num_workers) as executor:
futures = {executor.submit(analyze_file, fp): fp for fp in filepaths}
for future in as_completed(futures):
results.append(future.result())
return results
# 1000 files x 500ms / 8 cores = ~63 seconds
Step 7: Measure and Validate
After optimizing, measure the improvement and verify correctness.
Optimization Report Template
## Optimization Report
### Target
- **Function/Module**: {name}
- **Problem**: {what was slow or resource-heavy}
- **Requirement**: {target latency, throughput, or memory limit}
### Profiling Results (Before)
- **Execution time**: {time}
- **Memory usage**: {peak memory}
- **I/O operations**: {count and total time}
- **Bottleneck**: {specific hotspot identified by profiler}
### Optimization Applied
- **Technique**: {what was changed and why}
- **Complexity change**: O({before}) to O({after})
### Results (After)
- **Execution time**: {time} ({percentage improvement})
- **Memory usage**: {peak memory} ({percentage change})
- **I/O operations**: {count and total time}
- **Correctness verified**: {test suite passed, output comparison}
### Trade-offs
- **Readability impact**: {none / minor / significant}
- **Maintenance cost**: {none / minor / significant}
- **Additional dependencies**: {none / list}
Best Practices
- Always profile first: intuition about performance bottlenecks is wrong more often than it is right; measure to find the actual hotspot before changing any code
- Optimize the bottleneck, not the whole program: Amdahl's law dictates that optimizing code that accounts for 5% of execution time can yield at most a 5% improvement; focus on the dominant term
- Prefer algorithmic improvements over micro-optimizations: reducing O(n^2) to O(n log n) dwarfs any constant-factor improvement; always consider algorithmic complexity first
- Benchmark with realistic data: performance behavior often changes with data size and distribution; use production-representative data for benchmarks, not toy inputs
- Keep the original code as a reference: maintain the unoptimized version (commented or in version control) so that correctness can be verified by comparing outputs
- Document why the optimization was necessary: record the profiling results, the requirement that was not met, and the improvement achieved; without this context, future developers may simplify away the optimization
- Set performance budgets: define explicit latency, throughput, and memory targets before optimizing; stop when the target is met rather than pursuing diminishing returns
- Test edge cases after optimization: optimized code often handles edge cases differently; verify with empty inputs, single-element inputs, maximum-size inputs, and error conditions
Common Pitfalls
- Premature optimization: optimizing code before profiling it wastes effort on non-bottlenecks and reduces readability for no measurable benefit; profile first, always
- Optimizing for the wrong metric: reducing CPU time when the bottleneck is I/O, or reducing latency when the requirement is throughput; clarify which metric matters before optimizing
- Breaking correctness for performance: an optimization that produces wrong results faster is not an optimization; always verify outputs match the original after optimizing
- Ignoring cache invalidation: caching without a clear invalidation strategy leads to stale data bugs that are difficult to reproduce and diagnose; define invalidation rules before implementing the cache
- Over-parallelizing: adding more threads or processes than available CPU cores causes context-switching overhead that can make performance worse; match parallelism to available resources
- Micro-benchmarking without warm-up: JIT-compiled languages (Java, JavaScript) need warm-up iterations before benchmark measurements are representative; cold-start times are misleading
- Optimizing dead code paths: spending time optimizing error handlers, admin endpoints, or rarely-executed branches has negligible impact on real-world performance; focus on hot paths
- Not considering memory/CPU trade-offs: caching reduces CPU time but increases memory; parallelism reduces wall-clock time but increases total CPU time; understand and accept the trade-off
- Losing readability without measurement: if the "optimized" code is harder to understand but benchmarks show no significant improvement, revert to the readable version
What ships with it: 1 file
270 B alongside SKILL.md
agents/
- openai.yaml270 B