HeliosDB WASM Performance Tuning Guide
HeliosDB WASM Performance Tuning Guide
Overview
This guide covers performance optimization techniques for HeliosDB stored procedures, including:
- JIT compilation optimization
- Function result caching
- Parallel execution
- Memory management
- Profiling and benchmarking
Performance Architecture
Three-Tier Caching System
L1: Instance Pool (Hot instances, <1ms access)L2: Module Cache (Compiled modules, ~1ms access)L3: AOT Cache (Persistent disk cache, ~10ms access)Execution Pipeline
Request → Cache Check → Module Load → Instantiation → Execution → Result Cache | | | | | | <1µs <1ms ~1ms ~5ms varies <1msJIT Compilation Optimization
Optimization Levels
-- Fast compilation, less optimization (low latency)ALTER FUNCTION quick_functionSET jit_mode = 'low_latency';
-- Balanced (default)ALTER FUNCTION normal_functionSET jit_mode = 'balanced';
-- Aggressive optimization (best performance)ALTER FUNCTION heavy_computeSET jit_mode = 'fast';Performance Impact
| JIT Mode | Compilation Time | Execution Speed | Best For |
|---|---|---|---|
| low_latency | ~1ms | 1.0x | Short-lived functions |
| balanced | ~5ms | 1.5x | General purpose |
| fast | ~20ms | 2.0-3.0x | Long-running compute |
When to Use Each Mode
Low Latency:
- Functions called frequently with short execution time
- Request/response handlers
- Simple data transformations
Balanced (Default):
- General-purpose functions
- Mixed workloads
- Moderate execution time (10-100ms)
Fast:
- CPU-intensive algorithms
- Long-running computations (>100ms)
- Batch processing
Function Result Caching
Enable Caching
ALTER FUNCTION expensive_calculationSET cache_ttl = 300; -- Cache for 5 minutes
ALTER FUNCTION expensive_calculationSET cache_size = 1000; -- Maximum 1000 cached entriesCache Configuration
-- Aggressive cachingALTER FUNCTION lookup_functionSET cache_ttl = 3600, -- 1 hour cache_size = 10000, -- 10k entries cache_strategy = 'lru'; -- Least Recently Used eviction
-- Minimal cachingALTER FUNCTION volatile_functionSET cache_ttl = 10, -- 10 seconds cache_size = 100, cache_strategy = 'lru';Cache Performance
// First call: ~50ms (no cache)calculate_complex(42);
// Second call: <1ms (cache hit)calculate_complex(42);Cache Hit Rate
SELECT function_name, cache_hits, cache_misses, (cache_hits::float / (cache_hits + cache_misses)) * 100 as hit_rateFROM heliosdb.function_cache_statsORDER BY hit_rate DESC;When to Cache
Good Candidates:
- Pure functions (IMMUTABLE)
- Expensive computations
- External API calls
- Database aggregations
- Complex transformations
Poor Candidates:
- VOLATILE functions
- Random number generation
- Time-dependent calculations
- User-specific data (unless partitioned)
Parallel Execution
Batch Execution
// Sequential (slow): ~100ms totalfor (let i = 0; i < 10; i++) { await db.execute('process_item', [items[i]]);}
// Parallel (fast): ~10ms totalconst results = await db.executeParallel( items.map(item => ({ function: 'process_item', args: [item] })));Concurrency Control
-- Set maximum concurrent executionsALTER FUNCTION cpu_intensiveSET max_concurrency = 4;
-- Set queue size for pending executionsALTER FUNCTION cpu_intensiveSET queue_size = 100;Parallel Execution Patterns
Map-Reduce:
// Map phase (parallel)const mapped = await db.executeParallel( items.map(item => ({ function: 'map_function', args: [item] })));
// Reduce phase (sequential)const result = await db.execute('reduce_function', [mapped]);Pipeline:
// Stage 1: Parallel preprocessingconst preprocessed = await db.executeParallel( items.map(item => ({ function: 'preprocess', args: [item] })));
// Stage 2: Parallel processingconst processed = await db.executeParallel( preprocessed.map(item => ({ function: 'process', args: [item] })));
// Stage 3: Parallel postprocessingconst final = await db.executeParallel( processed.map(item => ({ function: 'postprocess', args: [item] })));Memory Management
Memory Optimization
// BAD: Creates many intermediate arraysfunction process_large_array(items) { return items .filter(x => x > 0) // New array .map(x => x * 2) // New array .map(x => ({ value: x })) // New array of objects .filter(x => x.value < 1000); // New array}
// GOOD: Single pass, minimal allocationsfunction process_large_array_optimized(items) { const result = []; for (let x of items) { if (x > 0) { const doubled = x * 2; if (doubled < 1000) { result.push({ value: doubled }); } } } return result;}Memory Limits
-- Increase memory limit for data-intensive functionALTER FUNCTION process_large_datasetSET max_memory_bytes = 67108864; -- 64MB
-- Decrease for lightweight functionALTER FUNCTION simple_calcSET max_memory_bytes = 4194304; -- 4MBStreaming Results
// BAD: Load entire result set into memoryfunction get_all_users() { const result = heliosdb.query('SELECT * FROM users'); return result.rows; // Could be millions of rows}
// GOOD: Stream resultsasync function* stream_users() { const BATCH_SIZE = 100; let offset = 0;
while (true) { const batch = heliosdb.query( 'SELECT * FROM users LIMIT ? OFFSET ?', [BATCH_SIZE, offset] );
if (batch.rows.length === 0) break;
for (let row of batch.rows) { yield row; }
offset += BATCH_SIZE; }}Profiling and Benchmarking
Built-in Profiling
-- Enable profilingALTER FUNCTION my_functionSET profiling = true;
-- Execute functionSELECT my_function(args);
-- View profiling resultsSELECT * FROM heliosdb.function_profile('my_function');Profiling output:
function_name | my_functiontotal_time_ms | 47.3compilation_time | 8.2execution_time | 35.1cache_time | 4.0memory_peak_mb | 12.5instructions | 45672134Performance Metrics
SELECT function_name, call_count, avg_execution_time_ms, p50_execution_time_ms, p95_execution_time_ms, p99_execution_time_ms, max_execution_time_ms, avg_memory_mb, cache_hit_rateFROM heliosdb.function_statsWHERE function_name = 'my_function';Benchmarking
// Benchmark helperfunction benchmark(fn, iterations = 100) { const start = Date.now();
for (let i = 0; i < iterations; i++) { fn(); }
const elapsed = Date.now() - start; const avg = elapsed / iterations;
heliosdb.log(`Benchmark: ${iterations} iterations in ${elapsed}ms (avg: ${avg}ms)`);}
// Usagebenchmark(() => expensive_calculation(42), 1000);Optimization Techniques
1. Minimize Function Calls
// BAD: Multiple function callsfunction process_items(items) { return items.map(item => { const validated = validate_item(item); // Function call const transformed = transform_item(validated); // Function call return formatted_item(transformed); // Function call });}
// GOOD: Inline processingfunction process_items_optimized(items) { return items.map(item => { // Inline validation if (!item || !item.id) return null;
// Inline transformation const transformed = { id: item.id, name: item.name.toUpperCase() };
// Inline formatting return `${transformed.id}: ${transformed.name}`; }).filter(x => x !== null);}2. Use Appropriate Data Structures
// BAD: O(n) lookup for each itemfunction find_users(user_ids) { const all_users = heliosdb.query('SELECT * FROM users').rows;
return user_ids.map(id => all_users.find(u => u.id === id) // O(n) for each );}
// GOOD: O(1) lookup with Mapfunction find_users_optimized(user_ids) { const all_users = heliosdb.query('SELECT * FROM users').rows; const user_map = new Map(all_users.map(u => [u.id, u]));
return user_ids.map(id => user_map.get(id));}3. Batch Database Operations
// BAD: Multiple queries (N+1 problem)function get_user_orders(user_ids) { return user_ids.map(user_id => { const orders = heliosdb.query( 'SELECT * FROM orders WHERE user_id = ?', [user_id] ).rows; return { user_id, orders }; });}
// GOOD: Single queryfunction get_user_orders_optimized(user_ids) { const placeholders = user_ids.map(() => '?').join(','); const all_orders = heliosdb.query( `SELECT * FROM orders WHERE user_id IN (${placeholders})`, user_ids ).rows;
// Group by user_id const grouped = new Map(); for (let order of all_orders) { if (!grouped.has(order.user_id)) { grouped.set(order.user_id, []); } grouped.get(order.user_id).push(order); }
return user_ids.map(user_id => ({ user_id, orders: grouped.get(user_id) || [] }));}4. Lazy Evaluation
// GOOD: Only compute what's neededfunction find_first_match(items, predicate) { for (let item of items) { if (predicate(item)) { return item; // Stop as soon as match found } } return null;}
// BAD: Compute everythingfunction find_first_match_bad(items, predicate) { const all_matches = items.filter(predicate); return all_matches[0] || null;}Performance Checklist
Before deploying to production:
- JIT mode configured appropriately
- Caching enabled for expensive functions
- Cache TTL and size tuned
- Resource limits configured
- Parallel execution used for batch operations
- Memory allocations minimized
- Database operations batched
- Appropriate data structures used
- Profiling data reviewed
- Benchmarks meet SLA requirements
Performance Targets
| Function Type | Target Latency | Target Throughput |
|---|---|---|
| Simple | <1ms | >10,000 req/s |
| Medium | <10ms | >1,000 req/s |
| Complex | <100ms | >100 req/s |
| Batch | <1s | >10 batches/s |
Troubleshooting
High Latency
- Check cache hit rate
- Review JIT optimization level
- Profile execution time breakdown
- Check for N+1 database queries
- Review memory allocations
Low Throughput
- Enable parallel execution
- Increase max_concurrency
- Pre-warm instance pool
- Batch requests
- Optimize hot paths
High Memory Usage
- Check for memory leaks
- Reduce data structure size
- Use streaming for large datasets
- Decrease memory limits
- Profile memory allocations
Conclusion
Performance optimization is an iterative process. Start with profiling to identify bottlenecks, apply appropriate optimizations, and measure the results. The three-tier caching system, JIT compilation options, and parallel execution capabilities provide powerful tools for achieving high performance while maintaining security and isolation.