Performance Documentation¶
This directory contains comprehensive performance documentation for the portfolio management system.
Quick Links¶
- Optimization Guide - Comprehensive optimization techniques and strategies
- Benchmarks - Detailed performance benchmark results
- Profiling Guide - How to profile and measure performance
- Existing Benchmarks - Links to specific benchmark documents
Overview¶
The portfolio management system is optimized for:
- Large universes: Efficiently handle 1000+ assets
- Long histories: Process 20+ years of daily data
- Frequent rebalancing: Support monthly/quarterly rebalancing
- Memory efficiency: Operate within typical system constraints
Key Performance Improvements¶
Summary Table¶
| Optimization | Component | Improvement | Impact |
|---|---|---|---|
| Lookback slicing | Backtest Engine | O(n²) → O(rebalances) | 95% fewer operations |
| Column filtering | Data Loading | 60% memory reduction | Faster loading |
| Streaming | Diagnostics | 44% memory reduction | 19.7 GB saved (70k files) |
| Vectorization | Asset Selection | 45-206x speedup | Sub-millisecond per asset |
| Factor caching | Preselection | 80-100% hit rate | 88% time savings |
Performance Highlights¶
- Preselection: < 100ms for 1000 assets with 252-day lookback
- Asset Selection: 189k assets/second processing speed
- Backtest: 5.2 seconds for 5-year monthly backtest with 100 assets
- Memory: < 200MB for 5000-asset universe preselection
Documentation Structure¶
Optimization Guide¶
Comprehensive guide covering all optimization techniques:
- Backtest Engine Optimization: Reduced from O(n²) to O(rebalances)
- Data Loading Optimization: Column and date filtering strategies
- Streaming Performance: Chunk-based processing for large files
- Asset Selection Vectorization: Replace row-wise operations with vectors
- Preselection Profiling: Performance characteristics and scalability
- Caching Strategies: Factor and price loader caching
- Best Practices: General optimization guidelines
Benchmarks¶
Detailed benchmark results for all components:
- Asset Selection: Vectorization improvements (45-206x speedup)
- Preselection: Scalability by universe size and lookback period
- Backtest Engine: Performance by strategy and rebalancing frequency
- Data Loading: Column and date filtering performance
- Streaming: Memory usage comparison across file sizes
- Caching: Hit rates and time savings
- System-Level: End-to-end backtest performance breakdown
Profiling Guide¶
How to profile and measure performance:
- Python Profiling Tools: cProfile, line_profiler, memory_profiler
- Benchmarking Best Practices: Warm-up, statistical significance, reproducibility
- Component-Specific Profiling: Backtest, preselection, data loading
- Automated Profiling: Benchmark suites and regression tests
- Visualization: Plotting and reporting results
- CI Integration: Continuous performance monitoring
Existing Benchmarks¶
Detailed Component Benchmarks¶
- Asset Selector Vectorization - 45-206x speedup details
- Preselection Profiling - Scalability analysis
- Caching Benchmarks - Cache performance and hit rates
- Fast I/O Benchmarks - Data loading optimizations
Quick Reference¶
Performance Targets¶
| Component | Target | Typical |
|---|---|---|
| Preselection (1000 assets) | < 100ms | 68ms |
| Asset Selection (10k rows) | < 100ms | 53ms |
| Backtest (5y, monthly) | < 10s | 5.2s |
| Data Load (1000 assets, 5y) | < 100ms | 68ms |
When to Optimize¶
Optimize when:
- Operation takes > 1 second for typical use
- Memory usage > 1 GB for typical dataset
- Users report performance issues
- Profiling shows clear bottleneck
Profile first, then optimize:
- Measure baseline performance
- Identify bottleneck via profiling
- Implement targeted optimization
- Verify correctness maintained
- Measure improvement
- Add performance regression test
Common Optimization Patterns¶
1. Vectorization¶
Replace row-wise operations with vectorized pandas/numpy operations:
2. Column Filtering¶
Load only needed columns to reduce memory:
# Load subset of columns
usecols = ["Date"] + needed_assets
df = pd.read_csv(file_path, usecols=usecols)
3. Streaming¶
Process large files in chunks:
# Process incrementally
for chunk in pd.read_csv(file_path, chunksize=10000):
result = process_chunk(chunk)
4. Caching¶
Cache expensive computations:
# Enable caching
cache = FactorCache(Path(".cache"), enabled=True)
preselection = Preselection(config, cache=cache)
5. Conditional Slicing¶
Only create expensive slices when needed:
Performance Testing¶
Running Benchmarks¶
# Run all benchmarks
python benchmarks/run_all.py
# Run specific benchmark
python benchmarks/benchmark_preselection.py
# Run with profiling
python -m cProfile -o output.prof benchmarks/benchmark_preselection.py
python -m pstats output.prof
Performance Regression Tests¶
# Run performance tests
pytest tests/performance/ -m performance -v
# Skip slow tests during development
pytest tests/ -m "not slow and not performance"
Monitoring Performance¶
Key Metrics to Track¶
- Execution Time: Total runtime for operations
- Memory Usage: Peak and average memory consumption
- Throughput: Operations per second
- Scalability: Performance vs. data size
- Cache Hit Rate: Percentage of cache hits
Performance History¶
Track performance over time:
- Baseline measurements before optimization
- After optimization improvements
- Regression test results over commits
Optimization Workflow¶
- Identify Problem: User reports or profiling reveals slowness
- Measure Baseline: Benchmark current performance
- Profile Code: Identify specific bottleneck
- Design Solution: Plan optimization approach
- Implement: Make targeted changes
- Verify Correctness: Ensure results unchanged
- Measure Improvement: Benchmark optimized version
- Document: Record changes and results
- Test: Add regression test to prevent backsliding
Configuration Recommendations¶
For Small Universes (< 100 assets)¶
# Simple configuration
config = PreselectionConfig(top_k=30, lookback=252)
preselection = Preselection(config) # No caching needed
For Medium Universes (100-1000 assets)¶
# Enable caching for better performance
cache = FactorCache(Path(".cache/factors"), enabled=True)
config = PreselectionConfig(top_k=50, lookback=252)
preselection = Preselection(config, cache=cache)
For Large Universes (> 1000 assets)¶
# Full optimization
cache = FactorCache(Path(".cache/factors"), enabled=True)
config = PreselectionConfig(
top_k=100,
lookback=252,
method=PreselectionMethod.MOMENTUM # Faster than COMBINED
)
preselection = Preselection(config, cache=cache)
# Load only needed columns
prices, returns = load_data(
prices_file,
returns_file,
assets=universe_assets, # Subset of total
start_date=start,
end_date=end
)
Troubleshooting Performance Issues¶
Slow Backtests¶
Check:
- Is caching enabled?
- Is rebalancing frequency appropriate?
- Is universe size too large?
- Are you loading full dataset when subset needed?
Solutions: See Troubleshooting Guide
High Memory Usage¶
Check:
- Are you loading full CSV files?
- Is price loader cache unbounded?
- Are you creating unnecessary data copies?
Solutions: See Optimization Guide
Slow Data Loading¶
Check:
- Loading all columns or just needed ones?
- Filtering date range during load?
- File size appropriate for memory?
Solutions: See Data Loading Optimization
Related Documentation¶
- Troubleshooting Guide - Debugging performance issues
- Best Practices - General development guidelines
- Caching Reliability - Cache behavior and guarantees
- Testing Guide - Performance testing strategies
Contributing¶
When adding new performance optimizations:
- Document baseline: Record performance before optimization
- Create benchmarks: Add repeatable benchmark scripts
- Add regression tests: Prevent performance backsliding
- Update docs: Document changes in this directory
- Share results: Add benchmark results to benchmarks.md