12 KiB
12 KiB
SpaceTime Compiler Plugin
Compile-time optimization tool that automatically identifies and applies space-time tradeoffs in Python code.
Features
- AST Analysis: Parse and analyze Python code for optimization opportunities
- Automatic Transformation: Convert algorithms to use √n memory strategies
- Safety Preservation: Ensure correctness while optimizing
- Static Memory Analysis: Predict memory usage before runtime
- Code Generation: Produce readable, optimized Python code
- Detailed Reports: Understand what optimizations were applied and why
Installation
# From sqrtspace-tools root directory
pip install ast numpy
Quick Start
Command Line Usage
# Analyze code for opportunities
python spacetime_compiler.py my_code.py --analyze-only
# Compile with optimizations
python spacetime_compiler.py my_code.py -o optimized_code.py
# Generate optimization report
python spacetime_compiler.py my_code.py -o optimized.py -r report.txt
# Run demonstration
python spacetime_compiler.py --demo
Programmatic Usage
from spacetime_compiler import SpaceTimeCompiler
compiler = SpaceTimeCompiler()
# Analyze a file
opportunities = compiler.analyze_file('my_algorithm.py')
for opp in opportunities:
print(f"Line {opp.line_number}: {opp.description}")
print(f" Memory savings: {opp.memory_savings}%")
# Transform code
with open('my_algorithm.py', 'r') as f:
code = f.read()
result = compiler.transform_code(code)
print(f"Memory reduction: {result.estimated_memory_reduction}%")
print(f"Optimized code:\n{result.optimized_code}")
Decorator Usage
from spacetime_compiler import optimize_spacetime
@optimize_spacetime()
def process_large_dataset(data):
# Original code
results = []
for item in data:
processed = expensive_operation(item)
results.append(processed)
return results
# Function is automatically optimized at definition time
# Will use √n checkpointing and streaming where beneficial
Optimization Types
1. Checkpoint Insertion
Identifies loops with accumulation and adds √n checkpointing:
# Before
total = 0
for i in range(1000000):
total += expensive_computation(i)
# After
total = 0
sqrt_n = int(np.sqrt(1000000))
checkpoint_total = 0
for i in range(1000000):
total += expensive_computation(i)
if i % sqrt_n == 0:
checkpoint_total = total # Checkpoint
2. Buffer Size Optimization
Converts fixed buffers to √n sizing:
# Before
buffer = []
for item in huge_dataset:
buffer.append(process(item))
if len(buffer) >= 10000:
flush_buffer(buffer)
buffer = []
# After
buffer_size = int(np.sqrt(len(huge_dataset)))
buffer = []
for item in huge_dataset:
buffer.append(process(item))
if len(buffer) >= buffer_size:
flush_buffer(buffer)
buffer = []
3. Streaming Conversion
Converts list comprehensions to generators:
# Before
squares = [x**2 for x in range(1000000)] # 8MB memory
# After
squares = (x**2 for x in range(1000000)) # ~0 memory
4. External Memory Algorithms
Replaces in-memory operations with external variants:
# Before
sorted_data = sorted(huge_list)
# After
sorted_data = external_sort(huge_list,
buffer_size=int(np.sqrt(len(huge_list))))
5. Cache Blocking
Optimizes matrix and array operations:
# Before
C = np.dot(A, B) # Cache thrashing for large matrices
# After
C = blocked_matmul(A, B, block_size=64) # Cache-friendly
How It Works
1. AST Analysis Phase
# The compiler parses code into Abstract Syntax Tree
tree = ast.parse(source_code)
# Custom visitor identifies patterns
analyzer = SpaceTimeAnalyzer()
analyzer.visit(tree)
# Returns list of opportunities with metadata
opportunities = analyzer.opportunities
2. Transformation Phase
# Transformer modifies AST nodes
transformer = SpaceTimeTransformer(opportunities)
optimized_tree = transformer.visit(tree)
# Generate Python code from modified AST
optimized_code = ast.unparse(optimized_tree)
3. Code Generation
- Adds necessary imports
- Preserves code structure and readability
- Includes comments explaining optimizations
- Maintains compatibility
Optimization Criteria
The compiler uses these criteria to decide on optimizations:
| Criterion | Weight | Description |
|---|---|---|
| Memory Savings | 40% | Estimated memory reduction |
| Time Overhead | 30% | Performance impact |
| Confidence | 20% | Certainty of analysis |
| Code Clarity | 10% | Readability preservation |
Automatic Selection Logic
def should_apply(opportunity):
if opportunity.confidence < 0.7:
return False # Too uncertain
if opportunity.memory_savings > 50 and opportunity.time_overhead < 100:
return True # Good tradeoff
if opportunity.time_overhead < 0:
return True # Performance improvement!
return False
Example Transformations
Example 1: Data Processing Pipeline
# Original code
def process_logs(log_files):
all_entries = []
for file in log_files:
entries = parse_file(file)
all_entries.extend(entries)
sorted_entries = sorted(all_entries, key=lambda x: x.timestamp)
aggregated = {}
for entry in sorted_entries:
key = entry.user_id
if key not in aggregated:
aggregated[key] = []
aggregated[key].append(entry)
return aggregated
# Compiler identifies:
# - Large accumulation in all_entries
# - Sorting operation on potentially large data
# - Dictionary building with lists
# Optimized code
def process_logs(log_files):
# Use generator to avoid storing all entries
def entry_generator():
for file in log_files:
entries = parse_file(file)
yield from entries
# External sort with √n memory
sorted_entries = external_sort(
entry_generator(),
key=lambda x: x.timestamp,
buffer_size=int(np.sqrt(estimate_total_entries()))
)
# Streaming aggregation
aggregated = {}
for entry in sorted_entries:
key = entry.user_id
if key not in aggregated:
aggregated[key] = []
aggregated[key].append(entry)
# Checkpoint large user lists
if len(aggregated[key]) % int(np.sqrt(len(aggregated[key]))) == 0:
checkpoint_user_data(key, aggregated[key])
return aggregated
Example 2: Scientific Computing
# Original code
def simulate_particles(n_steps, n_particles):
positions = np.random.rand(n_particles, 3)
velocities = np.random.rand(n_particles, 3)
forces = np.zeros((n_particles, 3))
trajectory = []
for step in range(n_steps):
# Calculate forces between all pairs
for i in range(n_particles):
for j in range(i+1, n_particles):
force = calculate_force(positions[i], positions[j])
forces[i] += force
forces[j] -= force
# Update positions
positions += velocities * dt
velocities += forces * dt / mass
# Store trajectory
trajectory.append(positions.copy())
return trajectory
# Optimized code
def simulate_particles(n_steps, n_particles):
positions = np.random.rand(n_particles, 3)
velocities = np.random.rand(n_particles, 3)
forces = np.zeros((n_particles, 3))
# √n checkpointing for trajectory
checkpoint_interval = int(np.sqrt(n_steps))
trajectory_checkpoints = []
current_trajectory = []
# Blocked force calculation for cache efficiency
block_size = min(64, int(np.sqrt(n_particles)))
for step in range(n_steps):
# Blocked force calculation
for i_block in range(0, n_particles, block_size):
for j_block in range(i_block, n_particles, block_size):
# Process block
for i in range(i_block, min(i_block + block_size, n_particles)):
for j in range(max(i+1, j_block),
min(j_block + block_size, n_particles)):
force = calculate_force(positions[i], positions[j])
forces[i] += force
forces[j] -= force
# Update positions
positions += velocities * dt
velocities += forces * dt / mass
# Checkpoint trajectory
current_trajectory.append(positions.copy())
if step % checkpoint_interval == 0:
trajectory_checkpoints.append(current_trajectory)
current_trajectory = []
# Reconstruct full trajectory on demand
return CheckpointedTrajectory(trajectory_checkpoints, current_trajectory)
Report Format
The compiler generates detailed reports:
SpaceTime Compiler Optimization Report
============================================================
Opportunities found: 5
Optimizations applied: 3
Estimated memory reduction: 87.3%
Estimated time overhead: 23.5%
Optimization Opportunities Found:
------------------------------------------------------------
1. [✓] Line 145: checkpoint
Large loop with accumulation - consider √n checkpointing
Memory savings: 95.0%
Time overhead: 20.0%
Confidence: 0.85
2. [✓] Line 203: external_memory
Sorting large data - consider external sort with √n memory
Memory savings: 93.0%
Time overhead: 45.0%
Confidence: 0.72
3. [✗] Line 67: streaming
Large list comprehension - consider generator expression
Memory savings: 99.0%
Time overhead: 5.0%
Confidence: 0.65 (Not applied: confidence too low)
4. [✓] Line 234: cache_blocking
Matrix operation - consider cache-blocked implementation
Memory savings: 0.0%
Time overhead: -30.0% (Performance improvement!)
Confidence: 0.88
5. [✗] Line 89: buffer_size
Buffer operations in loop - consider √n buffer sizing
Memory savings: 90.0%
Time overhead: 15.0%
Confidence: 0.60 (Not applied: confidence too low)
Integration with Build Systems
setup.py Integration
from setuptools import setup
from spacetime_compiler import compile_package
setup(
name='my_package',
cmdclass={
'build_py': compile_package, # Auto-optimize during build
}
)
Pre-commit Hook
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: spacetime-optimize
name: SpaceTime Optimization
entry: python -m spacetime_compiler
language: system
files: \.py$
args: [--analyze-only]
Safety and Correctness
The compiler ensures safety through:
- Conservative Transformation: Only applies high-confidence optimizations
- Semantic Preservation: Maintains exact program behavior
- Type Safety: Preserves type signatures and contracts
- Error Handling: Maintains exception behavior
- Testing: Recommends testing optimized code
Limitations
- Python Only: Currently supports Python AST only
- Static Analysis: Cannot optimize runtime-dependent patterns
- Import Dependencies: Optimized code may require additional imports
- Readability: Some optimizations may reduce code clarity
- Not All Patterns: Limited to recognized optimization patterns
Future Enhancements
- Support for more languages (C++, Java, Rust)
- Integration with IDEs (VS Code, PyCharm)
- Profile-guided optimization
- Machine learning for pattern recognition
- Automatic benchmark generation
- Distributed system optimizations
Troubleshooting
"Optimization not applied"
- Check confidence thresholds
- Ensure pattern matches expected structure
- Verify data size estimates
"Import errors in optimized code"
- Install required dependencies (external_sort, etc.)
- Check import statements in generated code
"Different behavior after optimization"
- File a bug report with minimal example
- Use --analyze-only to review planned changes
- Test with smaller datasets first
Contributing
To add new optimization patterns:
- Add pattern detection in
SpaceTimeAnalyzer - Implement transformation in
SpaceTimeTransformer - Add tests for correctness
- Update documentation
See Also
- SpaceTimeCore: Core calculations
- Profiler: Runtime profiling
- Benchmarks: Performance testing