Skip to main content

Overview

Shed Skin provides several mechanisms for optimizing program performance. This guide covers advanced optimization flags, manual optimization techniques, and profiling strategies.

Optimization Flags

Basic Optimization

Compile with standard optimizations:
This uses default C++ compiler flags: -O2 -march=native

Advanced Compiler Flags

Customize optimization with a flags file:
Create ~/.shedskin/flags/optimize:
Common flag combinations:

Profile-Guided Optimization (PGO)

PGO uses runtime profiling to guide optimization: Step 1: Compile with instrumentation
Create ~/.shedskin/flags/pgo-generate:
Step 2: Run with representative input
Step 3: Recompile with profile data
Create ~/.shedskin/flags/pgo-use:
Expected speedup: 5-30% depending on code characteristics

Understanding Generated C++ Code

Analyzing Generated Code

Examine the generated C++ to identify optimization opportunities:
What to look for:
  1. Unnecessary type conversions
  1. Virtual method calls
  1. Memory allocations
  1. Repeated computations

Hot Path Optimization

Focus optimization on code executed most frequently:
  1. Identify hot paths with profiling (see below)
  2. Rewrite hot functions in Python for better C++ generation
  3. Consider manual C++ for critical sections

Manual Optimization Techniques

List Comprehensions vs Loops

List comprehensions often generate more efficient code:
Generated C++ for comprehension is more optimized with pre-allocation.

Avoiding Dynamic Dispatch

Static types enable direct calls:
Type annotations help Shed Skin generate more efficient code.

Local Variables

Local variables are faster than globals or attributes:

Integer vs Float

Integer arithmetic is faster than floating-point:

String Building

Use join() for efficient string concatenation:

Container Pre-allocation

Pre-allocate when final size is known:

Memory Management Tuning

Garbage Collection Configuration

Tune the Boehm GC for your workload:
Common GC tuning:

Disabling GC

For programs with bounded memory usage:
Caution: Leaks are possible. Only use when:
  • Memory usage is bounded and known
  • Program runs for short duration
  • You’ve verified no leaks with testing

Object Pooling

Reuse objects instead of allocating:

Profiling

CPU Profiling

Use standard profiling tools on generated executable: perf (Linux):
Instruments (macOS):
Valgrind callgrind:

Identifying Bottlenecks

Look for:
  1. Hot functions - Functions consuming most CPU time
  2. Allocation patterns - Frequent memory allocations
  3. Cache misses - Poor memory access patterns
  4. Virtual calls - Polymorphic dispatch overhead

Python-Level Profiling

Profile before compilation to identify algorithmic issues:

Benchmarking

Micro-benchmarks

Measure specific operation performance:

Comparing Implementations

Automated Benchmarking

Advanced Tuning

Type Inference Limits

Adjust type inference parameters in shedskin/infer.py for large codebases:
Trade-offs:
  • Higher limits: More precise types, slower compilation, potentially faster runtime
  • Lower limits: Faster compilation, less precise types, potentially slower runtime

Compilation Time

For faster iteration during development:
Create ~/.shedskin/flags/debug:

Integer Size Selection

Choose appropriate integer size:

Optimization Checklist

Before optimizing:
  • Profile to identify actual bottlenecks
  • Verify correctness with tests
  • Establish baseline performance metrics
Code-level optimizations:
  • Use list comprehensions where appropriate
  • Cache frequently accessed attributes in locals
  • Pre-allocate containers when size is known
  • Use join() for string building
  • Prefer integer arithmetic when possible
  • Minimize dynamic dispatch with type hints
Compilation optimizations:
  • Use appropriate -O level (start with -O2)
  • Enable -march=native for CPU-specific optimizations
  • Consider PGO for critical applications
  • Profile generated C++ and adjust Python code accordingly
Memory optimizations:
  • Tune GC parameters for workload
  • Consider —nogc for bounded memory programs
  • Use object pooling for frequently allocated types
  • Monitor memory usage with valgrind

Expected Performance

Typical speedups over CPython:
  • Numeric code: 2-100x faster
  • String processing: 2-50x faster
  • General algorithms: 2-40x faster
  • With optimization: Additional 10-30% improvement
Actual results depend heavily on code characteristics.

Further Reading