Activation Thresholds
Epochly uses empirically-validated thresholds to determine when to activate each optimization level. This document explains these thresholds and how to tune them.
Overview
Activation thresholds control when Epochly enables different optimization strategies. These thresholds are based on benchmarks across thousands of workloads to ensure optimizations only apply when they provide real benefit.
Why Thresholds Matter
Optimization has overhead:
- Thread pool creation takes ~10ms
- JIT compilation takes ~100ms for first compile
- Worker spawning takes ~500ms
- GPU initialization takes ~200ms
If the workload doesn't exceed threshold minimums, the overhead exceeds the benefit.
Default Thresholds
Level 1: Threading Thresholds
| Threshold | Default Value | Purpose |
|---|---|---|
min_io_operations | 3 | Minimum I/O ops before threading |
min_operation_time_ms | 10 | Minimum time per operation |
min_total_time_ms | 50 | Minimum total workload time |
Level 2: JIT Thresholds
| Threshold | Default Value | Purpose |
|---|---|---|
hot_path_threshold | 1000 | Function calls before JIT compilation |
min_loop_iterations | 10000 | Minimum loop iterations for JIT benefit |
min_numerical_ops | 1000 | Minimum numerical operations |
Level 3: Multicore Thresholds
| Threshold | Default Value | Purpose |
|---|---|---|
min_data_size | 100000 | Minimum elements for parallelization |
min_computation_time_ms | 100 | Minimum computation time |
min_parallel_factor | 2 | Minimum parallelizable work units |
Level 4: GPU Thresholds
| Threshold | Default Value | Purpose |
|---|---|---|
min_array_size_bytes | 10000000 | Minimum 10MB for GPU offload |
min_operation_count | 100 | Minimum operations on GPU |
memory_ratio | 0.8 | Max GPU memory utilization |
Configuring Thresholds
Via Environment Variables
# Level 2: JIT thresholdexport EPOCHLY_JIT_HOT_PATH_THRESHOLD=500# Level 4: GPU thresholdexport EPOCHLY_GPU_WORKLOAD_THRESHOLD=5000000 # 5MB
Via TOML Configuration
[epochly.thresholds]hot_path_threshold = 500min_data_size = 50000gpu_workload_threshold = 5000000
Via Runtime API
import epochly# Set JIT thresholdepochly.configure(jit_hot_path_threshold=500)# Set GPU thresholdepochly.configure(gpu_workload_threshold=5_000_000)
Threshold Tuning Guide
When to Lower Thresholds
Lower thresholds when:
- Your workload is known to be parallelizable
- You have dedicated compute resources
- Latency is less important than throughput
- You're willing to accept higher startup overhead
When to Raise Thresholds
Raise thresholds when:
- Running many small operations
- Low latency is critical
- Memory is constrained
- Running on shared infrastructure
Empirical Threshold Data
Level 1 Threading Benchmarks
Threading becomes beneficial when I/O wait time exceeds threading overhead:
| I/O Operations | Threading Overhead | Net Benefit |
|---|---|---|
| 1 | 10ms | -10ms (slower) |
| 2 | 10ms | -5ms (slower) |
| 3 | 10ms | +5ms (faster) |
| 5 | 10ms | +50ms (faster) |
| 10 | 10ms | +200ms (faster) |
Level 2 JIT Benchmarks
JIT compilation provides benefit after sufficient calls to amortize compile time:
| Function Calls | Compile Time | Per-Call Savings | Break-Even |
|---|---|---|---|
| 100 | 100ms | 0.1ms | 1000 calls |
| 500 | 100ms | 0.1ms | 1000 calls |
| 1000 | 100ms | 0.1ms | Break-even |
| 5000 | 100ms | 0.1ms | 400ms benefit |
| 10000 | 100ms | 0.1ms | 900ms benefit |
Level 3 Multicore Benchmarks
Parallelization overhead depends on data size and transfer costs:
| Data Size | Parallelization Overhead | Processing Time | Net Benefit |
|---|---|---|---|
| 10K elements | 50ms | 10ms | -40ms |
| 50K elements | 50ms | 50ms | 0ms |
| 100K elements | 50ms | 100ms | +50ms |
| 1M elements | 50ms | 1000ms | +950ms |
Level 4 GPU Benchmarks
GPU benefits depend heavily on data transfer costs:
| Array Size | Transfer Time | GPU Compute | CPU Compute | Net Benefit |
|---|---|---|---|---|
| 1MB | 5ms | 1ms | 10ms | +4ms |
| 10MB | 50ms | 5ms | 100ms | +45ms |
| 100MB | 500ms | 50ms | 1000ms | +450ms |
| 1GB | 5000ms | 500ms | 10000ms | +4500ms |
Monitoring Threshold Effectiveness
Check Activation Statistics
import epochly# Get threshold statisticsstats = epochly.get_threshold_stats()print(f"JIT activations: {stats['jit_activations']}")print(f"JIT skips (below threshold): {stats['jit_skips']}")print(f"GPU activations: {stats['gpu_activations']}")print(f"GPU skips (below threshold): {stats['gpu_skips']}")
Analyze Threshold Decisions
import epochly# Enable threshold loggingepochly.configure(log_threshold_decisions=True)# Run workloadresult = my_function(data)# Check decisionsdecisions = epochly.get_threshold_decisions()for d in decisions:print(f"{d.level}: {d.decision} - {d.reason}")
Best Practices
1. Start with Defaults
Epochly's defaults are based on extensive benchmarking. Only change them if you have measured evidence that different values work better for your workload.
2. Profile Before Tuning
import epochly# Run profilingwith epochly.profile_context() as profile:result = my_function(data)# Analyze resultsif profile.suggests_lower_threshold('jit'):print("Consider lowering JIT threshold")
3. Test Both Directions
When tuning, test both higher and lower values:
import timeimport epochlyfor threshold in [100, 500, 1000, 2000, 5000]:epochly.configure(jit_hot_path_threshold=threshold)start = time.perf_counter()for _ in range(1000):my_function(data)elapsed = time.perf_counter() - startprint(f"Threshold {threshold}: {elapsed:.3f}s")
4. Consider Workload Variability
If your workload varies significantly, use more conservative (higher) thresholds to avoid overhead on small operations.
5. Monitor in Production
Enable threshold telemetry to understand real-world activation patterns:
export EPOCHLY_TELEMETRY=trueexport EPOCHLY_LOG_THRESHOLDS=true
Advanced: Custom Threshold Functions
For advanced use cases, you can provide custom threshold functions:
import epochlydef custom_gpu_threshold(data_size, operation_type):"""Custom function to decide GPU offload."""if operation_type == 'matmul':return data_size > 5_000_000 # 5MB for matmulelif operation_type == 'fft':return data_size > 1_000_000 # 1MB for FFTelse:return data_size > 10_000_000 # 10MB defaultepochly.configure(gpu_threshold_function=custom_gpu_threshold)
Troubleshooting
Optimization Not Activating
- Check current thresholds:
epochly.get_config() - Check workload size matches threshold requirements
- Enable debug logging:
export EPOCHLY_DEBUG=true
Optimization Causing Slowdown
- Workload may be below threshold - raise thresholds
- Check for excessive overhead with
epochly.profile_context() - Consider disabling specific levels:
epochly.set_level(2)
Inconsistent Performance
- Workload may be near threshold boundary
- Consider raising threshold to avoid activation jitter
- Use manual level control for critical paths